mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-02 19:17:10 +00:00
feat(folio): pin ios recipes to one simulator udid and ask when several match
This commit is contained in:
1 parent
419ed9d1c0
commit
c25265944e
2 files changed
+112
-22
No files matched your search
@@ -42,10 +42,18 @@ cheap to rebuild, so a lone local one is the single case worth guessing at.
|
|||||||
## iOS
|
## iOS
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
just ios # default device: iPhone 17 Pro
|
just ios # asks which simulator, unless one is booted
|
||||||
IOS_DEVICE="iPhone 15" just ios # pick a different simulator
|
IOS_DEVICE="iPhone 15" just ios # name it, by name or UDID
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`IOS_DEVICE` follows the same rule as `ANDROID_DEVICE`: a lone booted simulator
|
||||||
|
is taken without asking, anything else is asked about, and with no terminal to
|
||||||
|
ask on it refuses and lists what is installed. A name is matched against booted
|
||||||
|
simulators first and available ones second, and it can name several, since the
|
||||||
|
same iPhone exists under every installed runtime. When it does, you pick which,
|
||||||
|
and everything after that addresses the chosen UDID: the build destination, the
|
||||||
|
install, the launch and `--ios-device` all get the one simulator.
|
||||||
|
|
||||||
`just ios` regenerates `app/iosApp/iosApp.xcodeproj` from `app/iosApp/project.yml`,
|
`just ios` regenerates `app/iosApp/iosApp.xcodeproj` from `app/iosApp/project.yml`,
|
||||||
builds the KMP framework (`Shared.framework` from `:app:shared`), links it
|
builds the KMP framework (`Shared.framework` from `:app:shared`), links it
|
||||||
into the SwiftUI host, uninstalls any previous copy, installs, and launches.
|
into the SwiftUI host, uninstalls any previous copy, installs, and launches.
|
||||||
@@ -135,13 +143,18 @@ each pick was made.
|
|||||||
## Run a sanderling test (iOS)
|
## Run a sanderling test (iOS)
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
just test-ios # default simulator: iPhone 17 Pro
|
just test-ios # asks which simulator, unless one is booted
|
||||||
IOS_DEVICE="iPhone 15" just test-ios # pick a different simulator
|
IOS_DEVICE="iPhone 15" just test-ios # name it, by name or UDID
|
||||||
```
|
```
|
||||||
|
|
||||||
`just test-ios` boots the simulator if needed, runs `just ios` to install
|
`just test-ios` settles on a simulator once for the whole run, boots it if
|
||||||
and launch the app, then invokes `sanderling test --platform ios`. Same
|
needed, runs `just ios` to install and launch the app, then invokes `sanderling
|
||||||
`DURATION`, `SEED`, and `OUTPUT` env vars as the Android target.
|
test --platform ios`. Same `DURATION`, `SEED`, and `OUTPUT` env vars as the
|
||||||
|
Android target.
|
||||||
|
|
||||||
|
A physical iPhone is a different target: `just test-ios-device` requires
|
||||||
|
`IOS_DEVICE` to name it, and says so rather than running, because an empty one
|
||||||
|
resolves to a booted simulator and would fuzz that instead.
|
||||||
|
|
||||||
## How it connects to sanderling
|
## How it connects to sanderling
|
||||||
|
|
||||||
|
|||||||
+92
-15
@@ -7,7 +7,7 @@ android_device := env_var_or_default("ANDROID_DEVICE", "")
|
|||||||
duration := env_var_or_default("DURATION", "1m")
|
duration := env_var_or_default("DURATION", "1m")
|
||||||
seed := env_var_or_default("SEED", "0")
|
seed := env_var_or_default("SEED", "0")
|
||||||
output := env_var_or_default("OUTPUT", justfile_directory() / "sanderling" / "runs")
|
output := env_var_or_default("OUTPUT", justfile_directory() / "sanderling" / "runs")
|
||||||
ios_device := env_var_or_default("IOS_DEVICE", "iPhone 17 Pro")
|
ios_device := env_var_or_default("IOS_DEVICE", "")
|
||||||
ios_app := justfile_directory() / "app" / "iosApp" / "build" / "Build" / "Products" / "Debug-iphonesimulator" / "iosApp.app"
|
ios_app := justfile_directory() / "app" / "iosApp" / "build" / "Build" / "Products" / "Debug-iphonesimulator" / "iosApp.app"
|
||||||
ios_app_device := justfile_directory() / "app" / "iosApp" / "build" / "Build" / "Products" / "Debug-iphoneos" / "iosApp.app"
|
ios_app_device := justfile_directory() / "app" / "iosApp" / "build" / "Build" / "Products" / "Debug-iphoneos" / "iosApp.app"
|
||||||
apk := justfile_directory() / "app" / "androidApp" / "build" / "outputs" / "apk" / "debug" / "androidApp-debug.apk"
|
apk := justfile_directory() / "app" / "androidApp" / "build" / "outputs" / "apk" / "debug" / "androidApp-debug.apk"
|
||||||
@@ -248,33 +248,109 @@ clean:
|
|||||||
./gradlew clean
|
./gradlew clean
|
||||||
rm -rf app/iosApp/build
|
rm -rf app/iosApp/build
|
||||||
|
|
||||||
|
# Print the UDID of the simulator the iOS recipes drive. IOS_DEVICE names it by
|
||||||
|
# name or UDID, matched against booted simulators before merely available ones,
|
||||||
|
# the way sanderling matches --ios-device. A UDID rather than a name because the
|
||||||
|
# same iPhone exists under every installed runtime, and a seed only means
|
||||||
|
# something against one of them. Without IOS_DEVICE, a lone booted simulator is
|
||||||
|
# the one case taken without asking.
|
||||||
|
_require-ios-device:
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
rows="$(xcrun simctl list devices available | awk '
|
||||||
|
/^-- / { runtime = $0; sub(/^-- /, "", runtime); sub(/ --$/, "", runtime); next }
|
||||||
|
runtime ~ /^iOS/ && match($0, /\([0-9A-Fa-f]{8}-([0-9A-Fa-f]{4}-){3}[0-9A-Fa-f]{12}\)/) {
|
||||||
|
name = substr($0, 1, RSTART - 1)
|
||||||
|
gsub(/^[ \t]+|[ \t]+$/, "", name)
|
||||||
|
state = substr($0, RSTART + RLENGTH)
|
||||||
|
gsub(/[()[:space:]]/, "", state)
|
||||||
|
printf "%s\t%s\t%s\t%s\n", substr($0, RSTART + 1, RLENGTH - 2), name, runtime, state
|
||||||
|
}')"
|
||||||
|
|
||||||
|
catalog() {
|
||||||
|
printf '%s\n' "$rows" | awk -F'\t' 'NF { printf " %s (%s) %s%s\n", $2, $3, $1, ($4 == "Booted" ? " booted" : "") }'
|
||||||
|
}
|
||||||
|
|
||||||
|
refuse() {
|
||||||
|
cat >&2 <<EOF
|
||||||
|
folio: $1
|
||||||
|
|
||||||
|
$(catalog)
|
||||||
|
|
||||||
|
Set IOS_DEVICE to the simulator name or UDID to use. It can live in
|
||||||
|
examples/folio/.env:
|
||||||
|
|
||||||
|
IOS_DEVICE="iPhone 17 Pro" just test-ios
|
||||||
|
EOF
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ -z "$rows" ]]; then
|
||||||
|
echo "folio: no iOS simulator is available. Create one in Xcode, or see 'xcrun simctl list devices'." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "{{ios_device}}" ]]; then
|
||||||
|
matches="$(printf '%s\n' "$rows" | awk -F'\t' -v want="{{ios_device}}" '$4 == "Booted" && ($1 == want || $2 == want)')"
|
||||||
|
[[ -n "$matches" ]] || matches="$(printf '%s\n' "$rows" | awk -F'\t' -v want="{{ios_device}}" '$1 == want || $2 == want')"
|
||||||
|
[[ -n "$matches" ]] || refuse "IOS_DEVICE={{ios_device}} is not an available simulator."
|
||||||
|
else
|
||||||
|
matches="$(printf '%s\n' "$rows" | awk -F'\t' '$4 == "Booted"')"
|
||||||
|
[[ -n "$matches" ]] || matches="$rows"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$(printf '%s\n' "$matches" | grep -c .)" -eq 1 ]]; then
|
||||||
|
printf '%s\n' "$matches" | cut -f1
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
items="$(printf '%s\n' "$matches" | awk -F'\t' 'NF { printf "%s\t%s (%s) %s%s\n", $1, $2, $3, $1, ($4 == "Booted" ? " booted" : "") }')"
|
||||||
|
answer="$(FOLIO_PICK_ITEMS="$items" just _pick "folio: a run installs folio on the simulator, clears its state and drives it. Pick one:")"
|
||||||
|
case "$answer" in
|
||||||
|
"picked "*)
|
||||||
|
udid="${answer#picked }"
|
||||||
|
echo "folio: using $udid (IOS_DEVICE=$udid in examples/folio/.env skips this)" >&2
|
||||||
|
echo "$udid"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
cancelled)
|
||||||
|
echo "folio: cancelled." >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
refuse "more than one simulator answers to that, and there is no terminal here to ask on."
|
||||||
|
|
||||||
# Regenerate iosApp.xcodeproj from project.yml.
|
# Regenerate iosApp.xcodeproj from project.yml.
|
||||||
ios-gen:
|
ios-gen:
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
cd app/iosApp && xcodegen generate
|
cd app/iosApp && xcodegen generate
|
||||||
|
|
||||||
# Build + install + launch on a booted iOS simulator (boots IOS_DEVICE if none).
|
# Build + install + launch on the simulator given, or the one folio settles on.
|
||||||
ios:
|
ios udid="":
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
export ANDROID_HOME="$(just _android-home)"
|
export ANDROID_HOME="$(just _android-home)"
|
||||||
if ! xcrun simctl list devices booted | grep -q Booted; then
|
# Every step addresses this UDID rather than "booted", so the build, the
|
||||||
xcrun simctl boot "{{ios_device}}"
|
# install and the launch cannot land on different simulators.
|
||||||
|
udid="{{udid}}"
|
||||||
|
[[ -n "$udid" ]] || udid="$(just _require-ios-device)"
|
||||||
|
if ! xcrun simctl list devices booted | grep -q "$udid"; then
|
||||||
|
xcrun simctl boot "$udid"
|
||||||
open -a Simulator
|
open -a Simulator
|
||||||
sleep 3
|
sleep 3
|
||||||
fi
|
fi
|
||||||
just ios-gen
|
just ios-gen
|
||||||
xcodebuild -project app/iosApp/iosApp.xcodeproj -scheme iosApp \
|
xcodebuild -project app/iosApp/iosApp.xcodeproj -scheme iosApp \
|
||||||
-destination 'platform=iOS Simulator,name={{ios_device}}' \
|
-destination "platform=iOS Simulator,id=$udid" \
|
||||||
-derivedDataPath app/iosApp/build \
|
-derivedDataPath app/iosApp/build \
|
||||||
build | tail -5
|
build | tail -5
|
||||||
# Installing over the top keeps the data container, and folio's signed-in
|
# Installing over the top keeps the data container, and folio's signed-in
|
||||||
# session with it, so a run started straight after would open on the last
|
# session with it, so a run started straight after would open on the last
|
||||||
# run's Home screen instead of Login and diverge at step 1.
|
# run's Home screen instead of Login and diverge at step 1.
|
||||||
xcrun simctl uninstall booted app.folio || true
|
xcrun simctl uninstall "$udid" app.folio || true
|
||||||
xcrun simctl install booted "{{ios_app}}"
|
xcrun simctl install "$udid" "{{ios_app}}"
|
||||||
xcrun simctl launch booted app.folio
|
xcrun simctl launch "$udid" app.folio
|
||||||
|
|
||||||
# Build the folio app for a connected physical iOS device (signed via .env creds).
|
# Build the folio app for a connected physical iOS device (signed via .env creds).
|
||||||
# Signing reads ASC_API_* and DEVELOPMENT_TEAM/SANDERLING_IOS_TEAM from .env;
|
# Signing reads ASC_API_* and DEVELOPMENT_TEAM/SANDERLING_IOS_TEAM from .env;
|
||||||
@@ -357,17 +433,14 @@ web-build:
|
|||||||
test-ios:
|
test-ios:
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
just ios
|
udid="$(just _require-ios-device)"
|
||||||
ios_device_flag=()
|
just ios "$udid"
|
||||||
if [[ -n "{{ios_device}}" ]]; then
|
|
||||||
ios_device_flag=(--ios-device "{{ios_device}}")
|
|
||||||
fi
|
|
||||||
"{{sanderling}}" test \
|
"{{sanderling}}" test \
|
||||||
--platform ios \
|
--platform ios \
|
||||||
--spec "{{justfile_directory()}}/sanderling/spec.ts" \
|
--spec "{{justfile_directory()}}/sanderling/spec.ts" \
|
||||||
--bundle-id app.folio \
|
--bundle-id app.folio \
|
||||||
--ios-app-path "{{ios_app}}" \
|
--ios-app-path "{{ios_app}}" \
|
||||||
"${ios_device_flag[@]}" \
|
--ios-device "$udid" \
|
||||||
--duration "{{duration}}" \
|
--duration "{{duration}}" \
|
||||||
--seed "{{seed}}" \
|
--seed "{{seed}}" \
|
||||||
--output "{{output}}"
|
--output "{{output}}"
|
||||||
@@ -378,6 +451,10 @@ test-ios:
|
|||||||
test-ios-device:
|
test-ios-device:
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
if [[ -z "{{ios_device}}" ]]; then
|
||||||
|
echo "folio: set IOS_DEVICE to the connected device's name, UDID or CoreDevice id (see 'xcrun devicectl list devices'). Left empty, sanderling resolves a booted simulator and fuzzes that instead." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
just ios-device
|
just ios-device
|
||||||
"{{sanderling}}" test \
|
"{{sanderling}}" test \
|
||||||
--platform ios \
|
--platform ios \
|
||||||
|
|||||||
Reference in new issue
Block a user