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