mattermost-mobile-upstream/detox/scripts/preboot_ios_simulator.sh
yasser khan fca832c684
Some checks are pending
ci / lint-typecheck (push) Waiting to run
ci / detox-lint-typecheck (push) Waiting to run
ci / test (push) Waiting to run
fix(e2e): stabilize CMT and main Detox/Maestro E2E flakes (#10301)
* fix(e2e): preserve iOS App Group container metadata across shard specs and use swipe helper in Edit Server spec

* fix(maestro): dismiss iOS QuickPath keyboard onboarding and retry empty server URL input

* fix(e2e): avoid replaying single-use FormData streams on HTML interstitials and add ABAC/login transport retries

* fix(e2e): classify disabled-feature and server-timeout errors as infra in CMT notifications

* fix(e2e): address CodeRabbit review on container cache, ABAC cleanup and Maestro connect scroll

* fix(e2e): wait for restored denied state in upload permission spec as well

* fix(e2e): confirm restored ABAC decision server-side and scroll to URL label before Maestro recovery

* fix(e2e): return to channel list before ABAC cleanup logout and drop optional flag on recovery scroll

* fix(e2e): retry ABAC decision login on transport failures and classify CF 521/523 as transport failures

* test(e2e): cover apiUploadFileToChannel rebuilding FormData and stream on interstitial retry

* fix(e2e): budget ABAC decision waits for observed server flip latency

Co-authored-by: yasser khan <attitude3cena.yf@gmail.com>
2026-11-04 13:07:51 +05:30

253 lines
9.9 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
# Pre-boot an iOS simulator for Detox/Maestro CI.
#
# Optimized vs the former inline Detox workflow script:
# - One blocking bootstatus for sims that already have CoreSimulator dirs (typical CI).
# - Autofill plists written while shutdown — no "init boot just to mkdir" on warm sims.
# - Skips autofill re-configuration when already applied (marker + plist check).
# - Drops the redundant 8–10s bootstatus poll after bootstatus already completed.
# - Brand-new simulators (simctl create) still use boot → shutdown → configure → boot.
#
# Requires: DEVICE_NAME, DEVICE_OS_VERSION. Writes SIMULATOR_ID to GITHUB_ENV when set.
# Optional:
# PREBOOT_SKIP_PREWARM=1 — Maestro only (uses listapps readiness). Detox must pre-warm.
# PREBOOT_PREWARM_SECS — first pre-warm wait (default 15; iPad often needs 10–15s).
#
# Grants notifications only. Maestro's Calls flows need mic/camera and grant them
# themselves per batch (detox/maestro/scripts/run_ci_batches.sh
# grant_ios_calls_permissions), so this script does not — see grant_notifications
# for why extra privacy grants are avoided here.
set -euo pipefail
readonly BUNDLE_ID="com.mattermost.rnbeta"
readonly AUTOFILL_MARKER="mattermost-ci-autofill-v1"
readonly PREWARM_SECS="${PREBOOT_PREWARM_SECS:-15}"
readonly REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
log() {
echo "[preboot $(date +%H:%M:%S)] $*"
}
sim_state() {
xcrun simctl list devices 2>/dev/null | grep "$SIMULATOR_ID" | sed -E 's/.*\((Booted|Shutdown|Booting|Creating)\)$/\1/' || echo "Unknown"
}
shutdown_if_booted() {
if [ "$(sim_state)" = "Booted" ]; then
log "Shutting down simulator to edit autofill plists..."
xcrun simctl shutdown "$SIMULATOR_ID" || true
for _ in 1 2 3 4 5 6 7 8; do
[ "$(sim_state)" = "Shutdown" ] && return 0
sleep 0.5
done
log "Warning: simulator may not have reached Shutdown state"
fi
}
boot_and_wait() {
log "Booting simulator $SIMULATOR_ID..."
xcrun simctl boot "$SIMULATOR_ID" 2>/dev/null || true
log "Waiting for boot to complete (blocking bootstatus)..."
xcrun simctl bootstatus "$SIMULATOR_ID"
}
autofill_already_configured() {
[ -f "$AUTOFILL_STAMP" ] && return 0
[ -f "$SETTINGS_PLIST" ] || return 1
plutil -extract restrictedBool.allowPasswordAutoFill.value raw "$SETTINGS_PLIST" 2>/dev/null | grep -qi 'false'
}
configure_autofill_offline() {
log "Disabling password autofill (simulator shut down)..."
mkdir -p "$SETTINGS_DIR"
if ! (cd "$REPO_ROOT/detox" && node utils/disable_ios_autofill.js --simulator-id "$SIMULATOR_ID"); then
echo "Failed to disable password autofill"
exit 1
fi
touch "$AUTOFILL_STAMP"
}
seed_password_defaults() {
log "Seeding Passwords.app and keyboard onboarding defaults (best-effort)..."
xcrun simctl spawn "$SIMULATOR_ID" defaults write com.apple.Passwords AutoFill -bool NO 2>/dev/null || true
xcrun simctl spawn "$SIMULATOR_ID" defaults write com.apple.Passwords AutoSave -bool NO 2>/dev/null || true
xcrun simctl spawn "$SIMULATOR_ID" defaults write com.apple.Passwords credentialSaveNotificationsEnabled -bool NO 2>/dev/null || true
xcrun simctl spawn "$SIMULATOR_ID" defaults write com.apple.springboard AutoFillPasswords -bool NO 2>/dev/null || true
xcrun simctl spawn "$SIMULATOR_ID" defaults write -g DidShowContinuousPathIntroduction -bool YES 2>/dev/null || true
xcrun simctl spawn "$SIMULATOR_ID" defaults write com.apple.Preferences DidShowContinuousPathIntroduction -bool YES 2>/dev/null || true
}
install_app() {
local app_path
app_path=$(ls -d "$REPO_ROOT"/mobile-artifacts/*.app 2>/dev/null | head -1)
if [ -z "$app_path" ]; then
echo "No .app bundle found in mobile-artifacts/"
ls -la "$REPO_ROOT/mobile-artifacts/" || true
exit 1
fi
log "Installing $app_path..."
xcrun simctl install "$SIMULATOR_ID" "$app_path"
}
grant_notifications() {
# Match proven Detox CI: notifications only. Deny camera/photos corrupts TCC on iOS 26.x;
# keep grants minimal so a failed privacy call does not cascade into broken UI hit-testing.
log "Pre-granting notification permission..."
if ! xcrun simctl privacy "$SIMULATOR_ID" grant notifications "$BUNDLE_ID"; then
log "Warning: notification grant failed (continuing; Detox may re-request at launch)"
fi
}
kill_app_via_launchd() {
local app_pid
app_pid=$(xcrun simctl spawn "$SIMULATOR_ID" launchctl list 2>/dev/null | \
grep "$BUNDLE_ID" | awk '{print $1}' | grep -E '^[0-9]+$' || true)
if [ -z "$app_pid" ]; then
return 0
fi
xcrun simctl spawn "$SIMULATOR_ID" kill -9 "$app_pid" 2>/dev/null || true
# launchd teardown is asynchronous — poll until the bundle leaves the job list
# so a timeout is not reported as a successful kill.
for _ in $(seq 12); do
if ! xcrun simctl spawn "$SIMULATOR_ID" launchctl list 2>/dev/null | grep -q "$BUNDLE_ID"; then
log "Killed app via launchd (PID $app_pid)"
return 0
fi
sleep 0.25
done
log "Warning: app still listed in launchd 3s after kill -9 (PID $app_pid)"
return 1
}
prewarm_app() {
local sleep_time="${1:-$PREWARM_SECS}"
log "Pre-warming app (${sleep_time}s launch window)..."
xcrun simctl launch "$SIMULATOR_ID" "$BUNDLE_ID" 2>/dev/null &
local launch_pid=$!
sleep "$sleep_time"
kill "$launch_pid" 2>/dev/null || true
wait "$launch_pid" 2>/dev/null || true
if ! kill_app_via_launchd; then
log "Pre-warm cleanup did not complete — treating pre-warm as failed"
return 1
fi
if xcrun simctl get_app_container "$SIMULATOR_ID" "$BUNDLE_ID" data 2>/dev/null; then
log "Data container verified"
return 0
fi
log "Data container not found after pre-warm"
return 1
}
wait_ready_quick() {
# bootstatus already blocked until ready; one -b check avoids an extra fixed sleep.
if xcrun simctl bootstatus "$SIMULATOR_ID" -b 2>/dev/null; then
log "Simulator ready"
return 0
fi
log "Quick readiness check failed — waiting up to 5s..."
for _ in 1 2 3 4 5; do
sleep 1
xcrun simctl bootstatus "$SIMULATOR_ID" -b 2>/dev/null && return 0
done
log "Warning: simulator may not be fully ready"
return 0
}
verify_health() {
if ! xcrun simctl bootstatus "$SIMULATOR_ID" -b 2>/dev/null; then
log "Simulator unresponsive — rebooting..."
xcrun simctl shutdown "$SIMULATOR_ID" || true
sleep 2
boot_and_wait
open -a Simulator --args -CurrentDeviceUDID "$SIMULATOR_ID" || true
fi
if ! xcrun simctl get_app_container "$SIMULATOR_ID" "$BUNDLE_ID" 2>/dev/null; then
log "App missing — reinstalling..."
install_app
fi
sudo mdutil -a -i off 2>/dev/null || true
}
find_or_create_simulator() {
local device_name="${DEVICE_NAME:?DEVICE_NAME is required}"
local os_version="${DEVICE_OS_VERSION:?DEVICE_OS_VERSION is required}"
log "Looking for simulator: $device_name ($os_version)"
SIMULATOR_ID=$(xcrun simctl list devices | grep "$device_name" | grep "$os_version" | head -1 | grep -oE '([0-9A-F-]{36})' || true)
if [ -z "$SIMULATOR_ID" ]; then
SIMULATOR_ID=$(xcrun simctl list devices "$os_version" 2>/dev/null | grep "$device_name" | head -1 | grep -oE '([0-9A-F-]{36})' || true)
fi
if [ -n "$SIMULATOR_ID" ]; then
log "Found existing simulator: $SIMULATOR_ID"
return 0
fi
log "Creating simulator..."
local device_type runtime
device_type=$(xcrun simctl list devicetypes | grep "${device_name} (" | head -1 | awk -F'[()]' '{print $(NF-1)}')
runtime=$(xcrun simctl list runtimes | grep "$os_version" | head -1 | sed 's/.* - \(.*\)/\1/')
if [ -z "$device_type" ] || [ -z "$runtime" ]; then
echo "Could not resolve device type or runtime for $device_name / $os_version"
exit 1
fi
SIMULATOR_ID=$(xcrun simctl create "CI-${device_name}" "$device_type" "$runtime")
log "Created simulator: $SIMULATOR_ID"
export SIMULATOR_NEEDS_INIT_BOOT=1
}
# ─── Main ────────────────────────────────────────────────────────────────────
find_or_create_simulator
SETTINGS_DIR="$HOME/Library/Developer/CoreSimulator/Devices/$SIMULATOR_ID/data/Containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles/Library/ConfigurationProfiles"
SETTINGS_PLIST="$SETTINGS_DIR/UserSettings.plist"
AUTOFILL_STAMP="$SETTINGS_DIR/.$AUTOFILL_MARKER"
if autofill_already_configured; then
log "Autofill restrictions already configured — skipping plist edit"
if [ "$(sim_state)" != "Booted" ]; then
boot_and_wait
else
log "Simulator already booted"
fi
elif [ -n "${SIMULATOR_NEEDS_INIT_BOOT:-}" ] || [ ! -d "$SETTINGS_DIR" ]; then
# Fresh simulator: one boot creates system group dirs, then configure offline, then boot again.
log "New simulator — init boot to create CoreSimulator dirs..."
boot_and_wait
shutdown_if_booted
configure_autofill_offline
boot_and_wait
else
# Warm simulator on CI image: configure offline, single boot.
log "Warm simulator — applying autofill offline, then single boot"
shutdown_if_booted
configure_autofill_offline
boot_and_wait
fi
seed_password_defaults
install_app
grant_notifications
if [ "${PREBOOT_SKIP_PREWARM:-}" != "1" ]; then
if ! prewarm_app "$PREWARM_SECS"; then
log "Retrying pre-warm with 25s window..."
prewarm_app 25 || log "Pre-warm failed — first Detox launch may be slow"
fi
else
log "Skipping pre-warm (PREBOOT_SKIP_PREWARM=1)"
fi
open -a Simulator --args -CurrentDeviceUDID "$SIMULATOR_ID" || true
wait_ready_quick
verify_health
if [ -n "${GITHUB_ENV:-}" ]; then
echo "SIMULATOR_ID=$SIMULATOR_ID" >> "$GITHUB_ENV"
fi
log "Done. SIMULATOR_ID=$SIMULATOR_ID"