Matthias Nott
2026-07-07 d4d2fae4fd8699fac61a80905b7efbb594db03a2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#!/bin/bash
# PAILot iOS Deploy Script
#
# Installs a personal (Pro-unlocked) PAILot build onto one or more iOS devices.
#
# ── Remote / "fully remote" targets ────────────────────────────────────────
# Installation uses `xcrun devicectl device install`, which reaches the device
# over Apple's CoreDevice tunnel — USB, local network, OR any routable network
# path where the device is paired (e.g. across Tailscale). There is NO special
# remote logic to add: once a device has been paired, `--device <id>` works
# whether it sits on your desk or across the world, as long as it is awake and
# reachable. That is exactly how the iPad here installs — it is a `localNetwork`
# device, never plugged in. (First-time discovery of a NEW device uses .local /
# mDNS, which does not cross networks, so a device must be paired at least once
# on the local network before it can be driven fully remotely.)
# Check reachability any time with:  bash tools/deploy.sh --check
#
# ── Usage ──────────────────────────────────────────────────────────────────
#   bash tools/deploy.sh              # Matthias' iPhone (default)
#   bash tools/deploy.sh -a           # Amelie's iPhone
#   bash tools/deploy.sh -i           # Matthias' iPad
#   bash tools/deploy.sh --all        # every known device (continues on failure)
#   bash tools/deploy.sh --device ID  # explicit CoreDevice identifier (any remote device)
#   bash tools/deploy.sh --build      # force a fresh Pro-unlocked build first
#   bash tools/deploy.sh --check      # list devices devicectl can currently reach
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
APP_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
cd "$APP_DIR"
IPA="build/ios/ipa/pailot.ipa"
# Known devices — CoreDevice identifiers from `xcrun devicectl list devices`.
IPHONE_MATTHIAS="8708CADC-3330-50B6-AA0A-1655526A573A"
IPHONE_AMELIE="E3F823F4-8DC8-5C88-948E-C7FA2B4C4C72"
IPAD_MATTHIAS="8037E780-6BC0-5B9D-9ED4-D8F28DEEFD3F"
TARGETS=()          # entries: "identifier|label"
FORCE_BUILD=false
while [[ $# -gt 0 ]]; do
  case "$1" in
    -a|--amelie) TARGETS+=("$IPHONE_AMELIE|Amelie's iPhone"); shift ;;
    -i|--ipad)   TARGETS+=("$IPAD_MATTHIAS|Matthias' iPad"); shift ;;
    --iphone)    TARGETS+=("$IPHONE_MATTHIAS|Matthias' iPhone"); shift ;;
    --all)
      TARGETS+=("$IPHONE_MATTHIAS|Matthias' iPhone" \
                "$IPAD_MATTHIAS|Matthias' iPad" \
                "$IPHONE_AMELIE|Amelie's iPhone")
      shift ;;
    --device)    TARGETS+=("$2|device $2"); shift 2 ;;
    --build)     FORCE_BUILD=true; shift ;;
    --check)     echo "=== Devices devicectl can reach ==="; xcrun devicectl list devices; exit 0 ;;
    -h|--help)   sed -n '2,30p' "$0"; exit 0 ;;
    *) echo "Unknown option: $1" >&2; exit 2 ;;
  esac
done
# Default target: Matthias' iPhone.
if [[ ${#TARGETS[@]} -eq 0 ]]; then
  TARGETS+=("$IPHONE_MATTHIAS|Matthias' iPhone")
fi
# ── Build a Pro-unlocked personal IPA when needed ──────────────────────────
# PAILOT_PRO unlocks Pro for personal / sideloaded installs ONLY. App Store
# builds go through tools/build-appstore.sh / tools/release.sh, which do NOT
# pass this define — paying users still see the paywall.
if [[ "$FORCE_BUILD" == true || ! -f "$IPA" ]]; then
  echo "=== Building Pro-unlocked IPA (PAILOT_PRO=true) ==="
  flutter build ipa --release --no-pub --export-method development \
    --no-tree-shake-icons --dart-define=PAILOT_PRO=true
  ARCHIVE="build/ios/archive/Runner.xcarchive/Info.plist"
  if [ -f "$ARCHIVE" ]; then
    plutil -replace Name -string "PAILot" "$ARCHIVE" 2>/dev/null || true
    plutil -replace SchemeName -string "PAILot" "$ARCHIVE" 2>/dev/null || true
  fi
else
  echo "=== Using existing IPA: $IPA ==="
  echo "    (Pro status depends on how it was built — use --build to force a fresh Pro build)"
fi
install_one() {
  local id="$1" label="$2"
  echo ""
  echo "=== Installing on $label ($id) ==="
  if xcrun devicectl device install app --device "$id" "$IPA"; then
    echo "  OK: $label"
  else
    echo "  WARNING: install on $label failed — is it awake & reachable?"
    echo "           Try: bash tools/deploy.sh --check"
    return 1
  fi
}
fail=0
for t in "${TARGETS[@]}"; do
  install_one "${t%%|*}" "${t#*|}" || fail=1
done
echo ""
if [[ $fail -eq 0 ]]; then
  echo "=== Done — force-quit and reopen PAILot on the device(s). ==="
else
  echo "=== Done, with failures (see WARNINGs above). ==="
  exit 1
fi