field_ios

In-the-field app for Radroots on iOS
git clone https://radroots.dev/git/field_ios.git
Log | Files | Refs | README | LICENSE

xcode.sh (22437B)


      1 #!/bin/bash
      2 set -euo pipefail
      3 
      4 for variable in XCODE_DERIVED_DATA XCODE_SOURCE_PACKAGES XCODE_PACKAGE_CACHE; do
      5     if [[ -z "${!variable:-}" ]]; then
      6         echo "error: $variable is required; run this command through cargo extbuild" >&2
      7         exit 1
      8     fi
      9 done
     10 
     11 output_args=(
     12     -quiet
     13     -derivedDataPath "$XCODE_DERIVED_DATA"
     14     -clonedSourcePackagesDirPath "$XCODE_SOURCE_PACKAGES"
     15     -packageCachePath "$XCODE_PACKAGE_CACHE"
     16 )
     17 offline_args=(
     18     -disableAutomaticPackageResolution
     19     -onlyUsePackageVersionsFromResolvedFile
     20     -skipPackagePluginValidation
     21 )
     22 
     23 operation=${1:-}
     24 case "$operation" in
     25     resolve)
     26         exec xcodebuild \
     27             -resolvePackageDependencies \
     28             -project Tera.xcodeproj \
     29             -scheme Tera \
     30             "${output_args[@]}"
     31         ;;
     32     package-build)
     33         exec xcodebuild \
     34             -scheme TeraApp \
     35             -destination 'generic/platform=iOS Simulator' \
     36             "${output_args[@]}" \
     37             "${offline_args[@]}" \
     38             ARCHS=arm64 \
     39             build
     40         ;;
     41     package-test)
     42         destination=${2:?package-test requires a simulator destination}
     43         exec xcodebuild \
     44             -scheme TeraAppPublicAPITests \
     45             -destination "$destination" \
     46             "${output_args[@]}" \
     47             "${offline_args[@]}" \
     48             test
     49         ;;
     50     project-build)
     51         configuration=${2:?project-build requires a configuration}
     52         case "$configuration" in
     53             Debug|Release) ;;
     54             *) echo "error: unsupported configuration: $configuration" >&2; exit 1 ;;
     55         esac
     56         exec xcodebuild \
     57             -project Tera.xcodeproj \
     58             -scheme Tera \
     59             -configuration "$configuration" \
     60             -destination 'generic/platform=iOS Simulator' \
     61             "${output_args[@]}" \
     62             "${offline_args[@]}" \
     63             build
     64         ;;
     65     project-test)
     66         if [[ $# -lt 3 || $# -gt 4 ]]; then
     67             echo "error: project-test requires destination, target and optional UI selector" >&2
     68             exit 64
     69         fi
     70         destination=${2:?project-test requires a simulator destination}
     71         test_target=${3:?project-test requires a test target}
     72         case "$test_target" in
     73             TeraTests|TeraUITests) ;;
     74             *) echo "error: unsupported test target: $test_target" >&2; exit 1 ;;
     75         esac
     76         selector=${4:-}
     77         selected_target=$test_target
     78         if [[ -n "$selector" ]]; then
     79             if [[ "$test_target" != TeraUITests || ! "$selector" =~ ^([A-Za-z][A-Za-z0-9]*UITests)(/(test[A-Za-z0-9]+))?$ ]]; then
     80                 echo "error: unsupported UI test selector" >&2
     81                 exit 64
     82             fi
     83             test_class=${BASH_REMATCH[1]}
     84             test_method=${BASH_REMATCH[3]:-}
     85             repo_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
     86             test_source="$repo_root/TeraUITests/$test_class.swift"
     87             if [[ ! -f "$test_source" ]] || ! grep -Eq "^final class $test_class: XCTestCase" "$test_source"; then
     88                 echo "error: UI test class is not an owned test source" >&2
     89                 exit 64
     90             fi
     91             if [[ -n "$test_method" ]] && ! grep -Eq "^[[:space:]]+func $test_method\(\)" "$test_source"; then
     92                 echo "error: UI test method is not an owned test source" >&2
     93                 exit 64
     94             fi
     95             selected_target="$test_target/$selector"
     96         fi
     97         exec xcodebuild \
     98             -project Tera.xcodeproj \
     99             -scheme Tera \
    100             -destination "$destination" \
    101             "${output_args[@]}" \
    102             "${offline_args[@]}" \
    103             "-only-testing:$selected_target" \
    104             test
    105         ;;
    106     physical-app-build)
    107         destination=${2:?physical app build requires a device destination}
    108         xcconfig=${3:?physical app build requires an xcconfig}
    109         physical_automation=${TERA_IOS_PHYSICAL_AUTOMATION:-}
    110         development_team=${TERA_IOS_DEVELOPMENT_TEAM:?TERA_IOS_DEVELOPMENT_TEAM is required}
    111         if [[ "$physical_automation" != "1" ]]; then
    112             echo "error: physical app build requires TERA_IOS_PHYSICAL_AUTOMATION=1" >&2
    113             exit 64
    114         fi
    115         if [[ ! "$destination" =~ ^id=[A-Fa-f0-9-]+$ ]]; then
    116             echo "error: physical app build destination must be one exact device id" >&2
    117             exit 64
    118         fi
    119         if [[ ! "$development_team" =~ ^[A-Z0-9]{10}$ ]]; then
    120             echo "error: physical app build development team is invalid" >&2
    121             exit 64
    122         fi
    123         if [[ "$xcconfig" != "$XCODE_DERIVED_DATA"/* || ! -f "$xcconfig" ]]; then
    124             echo "error: physical app build xcconfig must be a regular file under XCODE_DERIVED_DATA" >&2
    125             exit 64
    126         fi
    127         device_id=${destination#id=}
    128         lock_state_file=$(mktemp "${TMPDIR:-/tmp}/tera-ios-lock-state.XXXXXX")
    129         trap 'unlink "$lock_state_file"' EXIT
    130         if ! xcrun devicectl device info lockState \
    131             --device "$device_id" \
    132             --quiet \
    133             --timeout 10 \
    134             --json-output "$lock_state_file"
    135         then
    136             echo "error: physical app build device lock state is unavailable" >&2
    137             exit 1
    138         fi
    139         passcode_required=$(/usr/bin/plutil \
    140             -extract result.passcodeRequired raw -o - "$lock_state_file")
    141         unlocked_since_boot=$(/usr/bin/plutil \
    142             -extract result.unlockedSinceBoot raw -o - "$lock_state_file")
    143         if [[ "$passcode_required" != "false" || "$unlocked_since_boot" != "true" ]]; then
    144             echo "error: physical app build device is locked; refusing to invoke Xcode" >&2
    145             exit 1
    146         fi
    147         exec xcodebuild \
    148             -project Tera.xcodeproj \
    149             -scheme Tera \
    150             -configuration Debug \
    151             -destination "$destination" \
    152             -xcconfig "$xcconfig" \
    153             "${output_args[@]}" \
    154             "${offline_args[@]}" \
    155             "DEVELOPMENT_TEAM=$development_team" \
    156             CODE_SIGN_STYLE=Automatic \
    157             "CODE_SIGN_IDENTITY=Apple Development" \
    158             build
    159         ;;
    160     local-social-ui-test)
    161         destination=${2:?local-social-ui-test requires a simulator destination}
    162         result_name=${3:?local-social-ui-test requires a result name}
    163         scenario=${4:-five-flow}
    164         qualification_run_id=${TERA_IOS_UI_TEST_RUN_ID:?TERA_IOS_UI_TEST_RUN_ID is required}
    165         relay_port=${TERA_IOS_LOCAL_SOCIAL_RELAY_PORT:-21000}
    166         blossom_port=${TERA_IOS_LOCAL_SOCIAL_BLOSSOM_PORT:-21100}
    167         if [[ ! "$destination" =~ ^platform=iOS\ Simulator,id=[A-Fa-f0-9-]+$ ]]; then
    168             echo "error: local-social-ui-test destination must be one exact simulator id" >&2
    169             exit 64
    170         fi
    171         if [[ ! "$result_name" =~ ^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$ ]]; then
    172             echo "error: local-social-ui-test result name is invalid" >&2
    173             exit 64
    174         fi
    175         if [[ ! "$qualification_run_id" =~ ^[a-z0-9][a-z0-9-]{6,62}[a-z0-9]$ ]]; then
    176             echo "error: local-social-ui-test run id is invalid" >&2
    177             exit 64
    178         fi
    179         for port in "$relay_port" "$blossom_port"; do
    180             if [[ ! "$port" =~ ^[0-9]+$ ]] || ((port < 1024 || port > 65535)); then
    181                 echo "error: local-social-ui-test port is invalid" >&2
    182                 exit 64
    183             fi
    184         done
    185         if [[ "$relay_port" == "$blossom_port" ]]; then
    186             echo "error: local-social-ui-test ports must be distinct" >&2
    187             exit 64
    188         fi
    189         case "$scenario" in
    190             five-flow)
    191                 test_selector=TeraUITests/TeraRemoteQualificationUITests/testLocalSocialFiveFlowScenario
    192                 evidence_command=verify
    193                 simulator_id=${destination##*id=}
    194                 previous_content_size=
    195                 requested_content_size=large
    196                 ;;
    197             accessibility)
    198                 test_selector=TeraUITests/TeraRemoteQualificationUITests/testLocalSocialAccessibilitySemantics
    199                 evidence_command=verify-accessibility
    200                 simulator_id=${destination##*id=}
    201                 previous_content_size=
    202                 requested_content_size=accessibility-extra-extra-extra-large
    203                 persona_fixture=
    204                 persona_result=
    205                 ;;
    206             persona)
    207                 test_selector=TeraUITests/TeraRemoteQualificationUITests/testLocalSocialDeterministicPersonas
    208                 evidence_command=verify-persona
    209                 simulator_id=${destination##*id=}
    210                 previous_content_size=
    211                 requested_content_size=accessibility-extra-extra-extra-large
    212                 persona_fixture=test-fixtures/local-social-personas.v1.json
    213                 persona_result="$XCODE_RESULTS/$result_name.persona-result.json"
    214                 ;;
    215             *)
    216                 echo "error: unsupported local-social-ui-test scenario: $scenario" >&2
    217                 exit 64
    218                 ;;
    219         esac
    220         : "${XCODE_RESULTS:?XCODE_RESULTS is required}"
    221         mkdir -p "$XCODE_RESULTS"
    222         result_bundle="$XCODE_RESULTS/$result_name.xcresult"
    223         evidence="$XCODE_RESULTS/$result_name.fixture.json"
    224         ready="$XCODE_RESULTS/$result_name.ready.json"
    225         control="$XCODE_RESULTS/$result_name.enable-uploads"
    226         if [[ -e "$result_bundle" || -e "$evidence" || -e "$ready" || -e "$control" || ( -n "${persona_result:-}" && -e "$persona_result" ) ]]; then
    227             echo "error: local-social-ui-test result already exists: $result_name" >&2
    228             exit 1
    229         fi
    230         if [[ "$scenario" == persona ]]; then
    231             if [[ -n "$(git status --porcelain --untracked-files=all)" ]]; then
    232                 echo "error: persona qualification requires one clean exact source tree" >&2
    233                 exit 1
    234             fi
    235             source_commit=$(git rev-parse HEAD)
    236             source_tree=$(git rev-parse 'HEAD^{tree}')
    237             upstream=$(git rev-parse '@{upstream}')
    238             xcode_identity=$(xcodebuild -version | tr '\n' ' ')
    239             simulator_sdk_build=$(xcrun --sdk iphonesimulator --show-sdk-build-version)
    240             app_build_sha256=$(
    241                 printf 'radroots.ios.local-social.app-build.v1\0%s\0%s\0%s\0%s\0%s\0%s\0%s\0' \
    242                     "$source_commit" "$source_tree" TeraUITests test Debug \
    243                     "$xcode_identity" "$simulator_sdk_build" \
    244                     | shasum -a 256 | awk '{print $1}'
    245             )
    246             persona_repair_commit=${TERA_IOS_UI_TEST_FORWARD_REPAIR_COMMIT:-}
    247             if [[ "$source_commit" != "$upstream" ]]; then
    248                 echo "error: persona qualification source is not equal to its configured upstream" >&2
    249                 exit 1
    250             fi
    251             if [[ -n "$persona_repair_commit" ]]; then
    252                 if [[ ! "$persona_repair_commit" =~ ^[0-9a-f]{40}$ ]] || \
    253                     ! git merge-base --is-ancestor "$persona_repair_commit" "$source_commit"
    254                 then
    255                     echo "error: persona forward-repair commit is invalid" >&2
    256                     exit 1
    257                 fi
    258             fi
    259             sh scripts/persona-verifier.sh verify-persona-fixture \
    260                 --fixture "$persona_fixture" \
    261                 --fixture-schema test-fixtures/local-social-personas.v1.schema.json \
    262                 --result-schema test-fixtures/local-social-persona-results.v1.schema.json \
    263                 --attempt-schema test-fixtures/local-social-persona-attempt-evidence.v1.schema.json \
    264                 --result-v2-schema test-fixtures/local-social-persona-results.v2.schema.json
    265             sh scripts/persona-verifier.sh serve \
    266                 --relay-port "$relay_port" \
    267                 --blossom-port "$blossom_port" \
    268                 --evidence "$evidence" \
    269                 --ready "$ready" \
    270                 --control "$control" \
    271                 --persona-fixture "$persona_fixture" &
    272         else
    273             sh scripts/persona-verifier.sh serve \
    274                 --relay-port "$relay_port" \
    275                 --blossom-port "$blossom_port" \
    276                 --evidence "$evidence" \
    277                 --ready "$ready" \
    278                 --control "$control" &
    279         fi
    280         fixture_pid=$!
    281         cleanup_fixture() {
    282             kill "$fixture_pid" 2>/dev/null || true
    283             wait "$fixture_pid" 2>/dev/null || true
    284             if [[ -n "$previous_content_size" ]]; then
    285                 xcrun simctl ui "$simulator_id" content_size "$previous_content_size"
    286                 previous_content_size=
    287             fi
    288         }
    289         trap cleanup_fixture EXIT INT TERM
    290         for _ in {1..100}; do
    291             [[ -f "$ready" ]] && break
    292             kill -0 "$fixture_pid" 2>/dev/null || {
    293                 echo "error: local-social fixture exited before readiness" >&2
    294                 exit 1
    295             }
    296             sleep 0.1
    297         done
    298         if [[ ! -f "$ready" ]]; then
    299             echo "error: local-social fixture readiness timed out" >&2
    300             exit 1
    301         fi
    302         xcrun simctl boot "$simulator_id" >/dev/null 2>&1 || true
    303         xcrun simctl bootstatus "$simulator_id" -b >/dev/null
    304         previous_content_size=$(xcrun simctl ui "$simulator_id" content_size)
    305         case "$previous_content_size" in
    306             extra-small|small|medium|large|extra-large|extra-extra-large|extra-extra-extra-large|accessibility-medium|accessibility-large|accessibility-extra-large|accessibility-extra-extra-large|accessibility-extra-extra-extra-large) ;;
    307             *)
    308                 echo "error: simulator content-size state is unavailable" >&2
    309                 exit 1
    310                 ;;
    311         esac
    312         xcrun simctl ui "$simulator_id" content_size "$requested_content_size"
    313         set +e
    314         xcodebuild \
    315             -project Tera.xcodeproj \
    316             -scheme Tera \
    317             -configuration Debug \
    318             -destination "$destination" \
    319             "${output_args[@]}" \
    320             "${offline_args[@]}" \
    321             -resultBundlePath "$result_bundle" \
    322             "-only-testing:$test_selector" \
    323             "TERA_IOS_UI_TEST_RUN_ID=$qualification_run_id" \
    324             "TERA_IOS_UI_TEST_NOSTR_RELAY_URLS=ws://127.0.0.1:$relay_port" \
    325             "TERA_IOS_UI_TEST_BLOSSOM_ORIGINS=http://127.0.0.1:$blossom_port" \
    326             "TERA_IOS_UI_TEST_FIXTURE_CONTROL=$control" \
    327             "TERA_IOS_UI_TEST_FIXTURE_EVIDENCE=$evidence" \
    328             "TERA_IOS_UI_TEST_NETWORK_PROFILE=simulator" \
    329             "TERA_IOS_UI_TEST_SOURCE_COMMIT=${source_commit:-}" \
    330             "TERA_IOS_UI_TEST_SOURCE_TREE=${source_tree:-}" \
    331             "TERA_IOS_UI_TEST_APP_BUILD_SHA256=${app_build_sha256:-}" \
    332             "TERA_IOS_UI_TEST_SIMULATOR_ID=$simulator_id" \
    333             test
    334         test_status=$?
    335         set -e
    336         cleanup_fixture
    337         trap - EXIT INT TERM
    338         if ((test_status != 0)); then
    339             exit "$test_status"
    340         fi
    341         if [[ "$scenario" == persona ]]; then
    342             persona_result_command=(sh scripts/persona-verifier.sh "$evidence_command" \
    343                 --fixture "$persona_fixture" \
    344                 --fixture-schema test-fixtures/local-social-personas.v1.schema.json \
    345                 --result-schema test-fixtures/local-social-persona-results.v1.schema.json \
    346                 --attempt-schema test-fixtures/local-social-persona-attempt-evidence.v1.schema.json \
    347                 --result-v2-schema test-fixtures/local-social-persona-results.v2.schema.json \
    348                 --evidence "$evidence" \
    349                 --result-bundle "$result_bundle" \
    350                 --output "$persona_result" \
    351                 --source-commit "$source_commit" \
    352                 --source-tree "$source_tree" \
    353                 --run-id "$qualification_run_id" \
    354                 --simulator-id "$simulator_id")
    355             if [[ -n "$persona_repair_commit" ]]; then
    356                 persona_result_command+=(--forward-repair-commit "$persona_repair_commit")
    357             fi
    358             "${persona_result_command[@]}"
    359         else
    360             sh scripts/persona-verifier.sh "$evidence_command" --evidence "$evidence"
    361         fi
    362         ;;
    363     remote-ui-test)
    364         destination=${2:?remote-ui-test requires a simulator destination}
    365         test_selector=${3:?remote-ui-test requires a TeraUITests selector}
    366         result_name=${4:?remote-ui-test requires a result name}
    367         qualification_run_id=${TERA_IOS_UI_TEST_RUN_ID:?TERA_IOS_UI_TEST_RUN_ID is required}
    368         blossom_origins=${TERA_IOS_UI_TEST_BLOSSOM_ORIGINS:?TERA_IOS_UI_TEST_BLOSSOM_ORIGINS is required}
    369         relay_urls=${TERA_IOS_UI_TEST_NOSTR_RELAY_URLS:-}
    370         if [[ ! "$destination" =~ ^platform=iOS\ Simulator,id=[A-Fa-f0-9-]+$ ]]; then
    371             echo "error: remote-ui-test destination must be one exact simulator id" >&2
    372             exit 64
    373         fi
    374         if [[ ! "$test_selector" =~ ^TeraUITests(/[-A-Za-z0-9_]+){0,2}$ ]]; then
    375             echo "error: remote-ui-test selector is invalid" >&2
    376             exit 64
    377         fi
    378         if [[ ! "$result_name" =~ ^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$ ]]; then
    379             echo "error: remote-ui-test result name is invalid" >&2
    380             exit 64
    381         fi
    382         if [[ ! "$qualification_run_id" =~ ^[a-z0-9][a-z0-9-]{6,62}[a-z0-9]$ ]]; then
    383             echo "error: remote-ui-test run id is invalid" >&2
    384             exit 64
    385         fi
    386         result_bundle="$XCODE_RESULTS/$result_name.xcresult"
    387         if [[ -e "$result_bundle" ]]; then
    388             echo "error: remote-ui-test result already exists: $result_bundle" >&2
    389             exit 1
    390         fi
    391         mkdir -p "$XCODE_RESULTS"
    392         exec xcodebuild \
    393             -project Tera.xcodeproj \
    394             -scheme Tera \
    395             -configuration Debug \
    396             -destination "$destination" \
    397             "${output_args[@]}" \
    398             "${offline_args[@]}" \
    399             -resultBundlePath "$result_bundle" \
    400             "-only-testing:$test_selector" \
    401             "TERA_IOS_UI_TEST_RUN_ID=$qualification_run_id" \
    402             "TERA_IOS_UI_TEST_NOSTR_RELAY_URLS=$relay_urls" \
    403             "TERA_IOS_UI_TEST_BLOSSOM_ORIGINS=$blossom_origins" \
    404             "TERA_IOS_UI_TEST_NETWORK_PROFILE=public" \
    405             test
    406         ;;
    407     physical-ui-build|physical-ui-test)
    408         destination=${2:?physical UI qualification requires a device destination}
    409         test_selector=${3:?physical UI qualification requires a TeraUITests selector}
    410         result_name=${4:-}
    411         physical_automation=${TERA_IOS_UI_TEST_PHYSICAL_AUTOMATION:-}
    412         development_team=${TERA_IOS_DEVELOPMENT_TEAM:?TERA_IOS_DEVELOPMENT_TEAM is required}
    413         qualification_run_id=${TERA_IOS_UI_TEST_RUN_ID:?TERA_IOS_UI_TEST_RUN_ID is required}
    414         blossom_origins=${TERA_IOS_UI_TEST_BLOSSOM_ORIGINS:?TERA_IOS_UI_TEST_BLOSSOM_ORIGINS is required}
    415         relay_urls=${TERA_IOS_UI_TEST_NOSTR_RELAY_URLS:-}
    416         if [[ "$physical_automation" != "1" ]]; then
    417             echo "error: physical UI qualification requires TERA_IOS_UI_TEST_PHYSICAL_AUTOMATION=1" >&2
    418             exit 64
    419         fi
    420         if [[ ! "$destination" =~ ^id=[A-Fa-f0-9-]+$ ]]; then
    421             echo "error: physical-ui-test destination must be one exact device id" >&2
    422             exit 64
    423         fi
    424         if [[ ! "$test_selector" =~ ^TeraUITests(/[-A-Za-z0-9_]+){0,2}$ ]]; then
    425             echo "error: physical-ui-test selector is invalid" >&2
    426             exit 64
    427         fi
    428         if [[ ! "$development_team" =~ ^[A-Z0-9]{10}$ ]]; then
    429             echo "error: physical-ui-test development team is invalid" >&2
    430             exit 64
    431         fi
    432         if [[ ! "$qualification_run_id" =~ ^[a-z0-9][a-z0-9-]{6,62}[a-z0-9]$ ]]; then
    433             echo "error: physical-ui-test run id is invalid" >&2
    434             exit 64
    435         fi
    436         device_id=${destination#id=}
    437         lock_state_file=$(mktemp "${TMPDIR:-/tmp}/tera-ios-lock-state.XXXXXX")
    438         trap 'unlink "$lock_state_file"' EXIT
    439         if ! xcrun devicectl device info lockState \
    440             --device "$device_id" \
    441             --quiet \
    442             --timeout 10 \
    443             --json-output "$lock_state_file"
    444         then
    445             echo "error: physical UI qualification device lock state is unavailable" >&2
    446             exit 1
    447         fi
    448         passcode_required=$(/usr/bin/plutil \
    449             -extract result.passcodeRequired raw -o - "$lock_state_file")
    450         unlocked_since_boot=$(/usr/bin/plutil \
    451             -extract result.unlockedSinceBoot raw -o - "$lock_state_file")
    452         if [[ "$passcode_required" != "false" || "$unlocked_since_boot" != "true" ]]; then
    453             echo "error: physical UI qualification device is locked; refusing to invoke Xcode" >&2
    454             exit 1
    455         fi
    456         physical_args=(
    457             -project Tera.xcodeproj \
    458             -scheme Tera \
    459             -configuration Debug \
    460             -destination "$destination" \
    461             "${output_args[@]}" \
    462             "${offline_args[@]}" \
    463             "-only-testing:$test_selector" \
    464             "DEVELOPMENT_TEAM=$development_team" \
    465             CODE_SIGN_STYLE=Automatic \
    466             "CODE_SIGN_IDENTITY=Apple Development" \
    467             "TERA_IOS_UI_TEST_RUN_ID=$qualification_run_id" \
    468             "TERA_IOS_UI_TEST_NOSTR_RELAY_URLS=$relay_urls" \
    469             "TERA_IOS_UI_TEST_BLOSSOM_ORIGINS=$blossom_origins" \
    470             "TERA_IOS_UI_TEST_NETWORK_PROFILE=public"
    471         )
    472         if [[ "$operation" == "physical-ui-build" ]]; then
    473             exec xcodebuild "${physical_args[@]}" build-for-testing
    474         fi
    475         if [[ ! "$result_name" =~ ^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$ ]]; then
    476             echo "error: physical-ui-test result name is invalid" >&2
    477             exit 64
    478         fi
    479         result_bundle="$XCODE_RESULTS/$result_name.xcresult"
    480         if [[ -e "$result_bundle" ]]; then
    481             echo "error: physical-ui-test result already exists: $result_bundle" >&2
    482             exit 1
    483         fi
    484         mkdir -p "$XCODE_RESULTS"
    485         exec xcodebuild \
    486             "${physical_args[@]}" \
    487             -resultBundlePath "$result_bundle" \
    488             test-without-building
    489         ;;
    490     *)
    491         echo "usage: $0 {resolve|package-build|package-test|project-build|project-test|local-social-ui-test|remote-ui-test|physical-ui-build|physical-ui-test}" >&2
    492         exit 64
    493         ;;
    494 esac