Инженерные практики

Проверка установки и запуска iOS-приложения на облачном Mac

Проверка установки и запуска iOS-приложения на облачном Mac

Конвейер iOS может без ошибок пройти весь этап компиляции, но лишь при установке в симулятор обнаружить неправильную структуру пакета. Возможен и другой сценарий: приложение устанавливается, запускается и сразу завершается. Если сразу переходить к полным UI-тестам, обратная связь будет медленной, а базовые неисправности станут выглядеть как тайм-ауты. Надёжнее добавить на облачном Mac быструю проверку установки и запуска продолжительностью две-три минуты: зафиксировать среду выполнения, установить только что собранный файл .app, убедиться, что целевой процесс продолжает работать, и сохранить журналы запуска.

На какие вопросы должна отвечать проверка

Эта проверка не тестирует бизнес-функции. Она отвечает только на четыре вопроса с ответами «да» или «нет»: может ли симулятор нормально запуститься, устанавливается ли артефакт, удаётся ли запустить указанный Bundle ID и остаётся ли процесс активным в течение периода наблюдения. Если хотя бы одна проверка завершается неудачно, не следует расходовать ресурсы на последующие UI-тесты.

Этап проверки Условие успеха Что сохранить при ошибке
Запуск симулятора bootstatus завершается нормально UDID устройства, версия среды выполнения
Установка simctl install возвращает 0 Путь к приложению, вывод команды
Запуск приложения simctl launch возвращает PID Bundle ID, код завершения
Проверка процесса После периода наблюдения процесс по-прежнему доступен для проверки Системные журналы, отчёты о сбоях

Ценность быстрой проверки не в расширении покрытия, а в получении двух чётких выводов: «артефакт существует» и «артефакт можно запустить».

UDID симулятора рекомендуется передавать задаче явно, не полагаясь на booted. На общем облачном Mac могут оставаться устройства, запущенные другими задачами. Неоднозначно заданная цель приведёт к смешению журналов и результатов установки. Конкретные доступные конфигурации узлов NowMini следует проверять в консоли; самому конвейеру требуются только установленный Xcode и соответствующая среда выполнения симулятора.

Сначала зафиксируйте артефакт сборки и целевое устройство

Сначала направьте результаты сборки в каталог задачи, чтобы не определять путь к ним в DerivedData. Scheme, конфигурация, Bundle ID и UDID должны передаваться через переменные окружения и проверяться в начале задачи.

set -euo pipefail

: "${SIM_UDID:?SIM_UDID is required}"
: "${APP_BUNDLE_ID:?APP_BUNDLE_ID is required}"

WORK_DIR="${RUNNER_TEMP:-$PWD/.smoke}"
APP_PATH="$WORK_DIR/build/Sample.app"
LOG_DIR="$WORK_DIR/logs"

rm -rf "$WORK_DIR"
mkdir -p "$LOG_DIR"

xcodebuild \
  -workspace Sample.xcworkspace \
  -scheme Sample \
  -configuration Debug \
  -sdk iphonesimulator \
  -destination "platform=iOS Simulator,id=$SIM_UDID" \
  CONFIGURATION_BUILD_DIR="$WORK_DIR/build" \
  build

Не используйте find, чтобы взять первый файл .app. Проект может одновременно создавать основное приложение, тестовый хост и расширения, а порядок результатов не гарантируется. Если имя продукта известно, сформируйте путь напрямую и перед установкой проверьте Info.plist:

test -d "$APP_PATH"
BUILT_ID=$(/usr/libexec/PlistBuddy -c \
  "Print :CFBundleIdentifier" "$APP_PATH/Info.plist")
test "$BUILT_ID" = "$APP_BUNDLE_ID"

Этот шаг позволяет заранее обнаружить неверно выбранный Scheme, ошибочную подстановку конфигурации сборки или ситуацию, когда команда установки получила тестовый хост вместо нужного приложения.

Запустите симулятор, установите приложение и проверьте процесс

При запуске устройства используйте bootstatus -b, чтобы дождаться фактической готовности системы. Если выполнить только boot и сразу перейти к установке, при высокой нагрузке могут возникать периодические ошибки из-за того, что службы ещё не готовы.

xcrun simctl boot "$SIM_UDID" 2>/dev/null || true
xcrun simctl bootstatus "$SIM_UDID" -b

xcrun simctl uninstall "$SIM_UDID" "$APP_BUNDLE_ID" \
  2>/dev/null || true
xcrun simctl install "$SIM_UDID" "$APP_PATH"

LAUNCH_OUTPUT=$(xcrun simctl launch \
  "$SIM_UDID" "$APP_BUNDLE_ID" \
  -SmokeTestMode YES)

printf '%s
' "$LAUNCH_OUTPUT"
PID=$(printf '%s
' "$LAUNCH_OUTPUT" | awk -F': ' 'NF == 2 {print $2}')
test -n "$PID"

Аргументы запуска могут позволить приложению пропустить первичную настройку, отключить анимацию или использовать локальные фикстуры. Однако такое поведение должно быть явно реализовано в коде приложения: нельзя считать, что произвольный аргумент автоматически подействует. После запуска подождите в течение короткого периода наблюдения, а затем проверьте процесс изнутри симулятора. Если учитывать только код возврата launch, можно пропустить завершение приложения в первую секунду после запуска.

sleep 8
xcrun simctl spawn "$SIM_UDID" launchctl print \
  "gui/$(id -u)/$APP_BUNDLE_ID" >/dev/null

У некоторых приложений метка процесса не полностью совпадает с Bundle ID. В этом случае следует проверять известное имя процесса либо при запуске приложения записывать маркер готовности, доступный только тестам. Не пытайтесь скрыть нестабильное состояние простым увеличением фиксированного времени ожидания.

Сохраняйте понятные диагностические данные при ошибках

Очистка важна, но сначала необходимо сохранить диагностические данные. Минимальный набор включает вывод сборки, описание устройства, вывод команд установки и запуска, а также унифицированные журналы, относящиеся к целевому процессу. Диапазон журналов должен охватывать текущую задачу, а не всю историю хост-машины.

xcrun simctl list devices -j > "$LOG_DIR/devices.json"

xcrun simctl spawn "$SIM_UDID" log show \
  --style compact \
  --last 2m \
  --predicate "process == 'Sample'" \
  > "$LOG_DIR/sample-launch.log" 2>&1 || true

Распространённая ошибка — архивировать только стандартный поток ошибок. Если приложение завершается сразу после запуска, настоящая причина может находиться в системных журналах симулятора: например, ошибка загрузки динамической библиотеки, неправильный путь к ресурсам или необработанное исключение. Если журналы содержат токены, заголовки запросов или пользовательские данные, перед архивацией их необходимо обезличить, а доступ к артефактам конвейера — ограничить.

Разделяйте ошибки среды и ошибки приложения

Ошибка bootstatus обычно указывает на проблему среды устройства. При ошибке install в первую очередь проверьте структуру пакета, совместимость со средой выполнения и свободное место на диске. Если launch завершается успешно, но процесс исчезает, сначала изучите журналы приложения. Решение о повторном запуске следует принимать только после классификации ошибки. Если приложение несколько раз подряд завершается одинаково, не нужно многократно перезапускать задачу автоматически: это лишь задержит получение достоверной обратной связи.

Очистите состояние и подключите проверку к конвейеру

Независимо от результата задачи приложение необходимо завершить, а использованный симулятор — выключить. Чтобы гарантировать выполнение завершающих действий, можно использовать shell-команду trap:

cleanup() {
  xcrun simctl terminate "$SIM_UDID" "$APP_BUNDLE_ID" \
    2>/dev/null || true
  xcrun simctl uninstall "$SIM_UDID" "$APP_BUNDLE_ID" \
    2>/dev/null || true
  xcrun simctl shutdown "$SIM_UDID" \
    2>/dev/null || true
}
trap cleanup EXIT

Если задача требует полностью идентичного начального состояния, после выключения можно выполнить simctl erase. Однако эта операция увеличивает время выполнения и удаляет предварительно загруженные данные. Более распространённый компромисс — удалить приложение, очистить каталог задачи и выделить отдельное устройство каждому конвейеру. Если проверка затрагивает разрешения, дополнительно выполните явный сброс соответствующих служб.

Наконец, разместите эту проверку после компиляции, но перед модульными или UI-тестами. Скрипт должен возвращать однозначный ненулевой код завершения, а шаг архивации журналов должен выполняться всегда. Тогда ошибка установки не будет маскироваться под тайм-аут теста, а приложение, завершающееся сразу после запуска, не займёт впустую всю группу исполнителей. Каждая ошибка оставит достаточно данных, чтобы разработчики могли воспроизвести её с тем же артефактом и в той же среде выполнения.

Часто задаваемые вопросы

Почему успешной команды xcodebuild недостаточно?

Она подтверждает создание продукта, но не гарантирует установку и запуск. Ошибки структуры пакета, минимальной версии системы или ресурсов могут проявиться только на этих этапах.

Какие данные нужно сохранить при сбое проверки?

Сохраните вывод xcodebuild, UDID и версию среды симулятора, путь к приложению, коды завершения simctl install и launch, а также журналы целевого процесса за период вокруг сбоя.

NowMini M4

Запустите следующую задачу на выделенном физическом узле

Облачный Mac с M4, 16 ГБ ОЗУ и SSD на 256 ГБ. Выберите узел в Сингапуре, Токио, Сеуле или Гонконге под срок задачи.

Арендовать облачный Mac