Технические заметки VMArm

Сборка и проверка iOS On-Demand Resources на облачном Mac

Сборка и проверка iOS On-Demand Resources на облачном Mac

После переноса уровней, текстур высокого разрешения или офлайн-моделей iOS-проекта в On-Demand Resources размер установочного пакета обычно заметно уменьшается. Однако успешная сборка еще не означает, что ресурсы доступны. Чаще всего проблемы возникают не из-за ошибок компиляции, а из-за пропущенных тегов, случайного включения всех ресурсов в тестовую сборку или несоответствия пакетов ресурсов в релизном артефакте запросам из кода. Облачный Mac позволяет оформить такие проверки как отдельное задание: каждый раз архивировать проект из чистей рабочей области, сохранять манифесты и контрольные суммы, а затем выполнять реальный запрос ресурсов.

Сначала определите критерии приемки

Приемка ODR должна охватывать как минимум три уровня: конфигурацию проекта, экспортированные артефакты и запросы во время выполнения. Проверка только одного из них неизбежно оставляет слепые зоны.

На уровне проекта нужно убедиться, что для Target включены On-Demand Resources, а каждый тег в каталоге ресурсов имеет четко определенное назначение. На уровне артефактов проверяются .assetpack, файлы манифестов и хеши файлов. На уровне выполнения необходимо проверить первый запрос, освобождение ресурсов и повторный запрос.

Запуска приложения недостаточно, чтобы считать ODR проверенными. Даже полное отсутствие ресурсов уровня, к которому еще не обращались, не помешает открыть начальный экран.

Для каждого тега рекомендуется сохранять четыре параметра: ответственного, экран-триггер, ожидаемый тип ресурсов и признак обязательности при первой установке. Эти сведения следует хранить в репозитории в виде обычного текстового манифеста, а не только в интерфейсе Xcode.

Управляйте разбиением с помощью стабильных тегов

Не следует создавать отдельный тег для каждой папки. При слишком мелком разбиении образуется множество небольших пакетов и возрастает частота запросов. При слишком крупном пользователю приходится загружать целую группу не связанных между собой ресурсов ради одного экрана. Надежнее группировать ресурсы по пользовательским сценариям, например level-01, tutorial-audio и model-basic, и следить, чтобы имена запросов в коде полностью совпадали с тегами в каталоге ресурсов.

Добавьте обязательную проверку манифеста тегов

В репозитории можно хранить список разрешенных тегов:

level-01
level-02
tutorial-audio
model-basic

Скрипт сборки должен извлекать теги из файла проекта или сгенерированных данных о ресурсах, сортировать их и сравнивать с этим списком. Добавление нового тега должно сопровождаться изменением манифеста. При удалении тега необходимо также проверить вызовы NSBundleResourceRequest в коде. Так опечатки будут выявляться еще на этапе слияния, а не во время выполнения.

В именах тегов следует использовать только стабильные символы ASCII, избегая пробелов, смешения регистра и временных номеров версий. Обновления ресурсов должны обозначаться версией содержимого или контрольной суммой. Не создавайте постоянно такие неподдерживаемые имена, как level-final-v2-new.

Зафиксируйте условия архивирования и экспорта

Для одного и того же коммита необходимо использовать фиксированные workspace, scheme, конфигурацию Release и параметры экспорта. Задание на узле VMArm может предварительно очищать каталог вывода текущего запуска, но не следует без разбора удалять глобальные кеши: иначе будет трудно определить, вызваны ли изменения ресурсами или окружением.

set -euo pipefail

: "${WORKSPACE:?WORKSPACE is required}"
: "${SCHEME:?SCHEME is required}"

ROOT="$PWD"
ARCHIVE_PATH="$ROOT/out/Game.xcarchive"
EXPORT_DIR="$ROOT/out/export"

rm -rf "$ARCHIVE_PATH" "$EXPORT_DIR"
mkdir -p "$EXPORT_DIR"

xcodebuild \
  -workspace "$WORKSPACE" \
  -scheme "$SCHEME" \
  -configuration Release \
  -destination 'generic/platform=iOS' \
  -archivePath "$ARCHIVE_PATH" \
  clean archive

xcodebuild \
  -exportArchive \
  -archivePath "$ARCHIVE_PATH" \
  -exportPath "$EXPORT_DIR" \
  -exportOptionsPlist "$ROOT/ci/ExportOptions.plist"

Для локальной приемки параметр embedOnDemandResourcesAssetPacksInBundle можно установить в true, чтобы сначала проверить содержимое отдельных пакетов и логику запросов. В производственном процессе следует использовать конфигурацию экспорта, соответствующую фактическому способу распространения, а не локальные настройки встраивания. Артефакты этих двух процессов необходимо сохранять в разных каталогах, чтобы тестовая сборка не перезаписала пакет, подготовленный к выпуску.

Выполните трехуровневую проверку артефактов

На первом уровне проверяется наличие файлов. На втором — возможность разбора plist. На третьем для всех файлов ресурсов создаются стабильные контрольные суммы, позволяющие сравнивать две сборки одного коммита.

set -euo pipefail

ARCHIVE_PATH="$PWD/out/Game.xcarchive"
EXPORT_DIR="$PWD/out/export"
REPORT_DIR="$PWD/out/report"

mkdir -p "$REPORT_DIR"

find "$ARCHIVE_PATH" "$EXPORT_DIR" \
  -name '*.assetpack' \
  -print | LC_ALL=C sort > "$REPORT_DIR/assetpacks.txt"

find "$ARCHIVE_PATH" "$EXPORT_DIR" \
  -name 'AssetPackManifest.plist' \
  -exec plutil -lint {} \; > "$REPORT_DIR/plist-lint.txt"

find "$ARCHIVE_PATH" "$EXPORT_DIR" \
  -type f \
  \( -name '*.car' -o -name '*.plist' -o -name '*.assetpack' \) \
  -exec shasum -a 256 {} \; |
  LC_ALL=C sort > "$REPORT_DIR/checksums.txt"

test -s "$REPORT_DIR/assetpacks.txt"

Если .assetpack фактически является каталогом, последняя команда хеширования не вычислит контрольные суммы его содержимого напрямую. Поэтому в проверку нужно также включить обычные файлы внутри этого каталога. В отчете следует указывать коммит, версию Xcode, scheme и хеш файла конфигурации экспорта, но не следует записывать закрытые ключи подписи, токены или полные пути к учетным данным.

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

Проверка во время выполнения и поиск неисправностей

Тестирование во время выполнения следует начинать из чистого состояния. После установки приемочной сборки запросите тег, не требуемый при первоначальной установке, и зарегистрируйте три события: начало запроса, получение разрешения на доступ и освобождение ресурсов. Затем завершите работу приложения, очистите тестовое окружение и повторите процедуру, чтобы убедиться, что успех не был связан со старым кешем.

Порядок диагностики распространенных симптомов

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

Не маскируйте ошибки бесконечными повторными попытками. Задайте для запроса явный тайм-аут. В журнале ошибок фиксируйте только тег, домен ошибки, код ошибки и этап — не выводите токены доступа или конфиденциальные пути. После завершения приемки сохраните отчет, хеш конфигурации экспорта и журнал ошибок, а временные пакеты и тестовые кеши удалите. Тогда при следующем отклонении после изменения ODR можно будет сразу сравнить артефакты, а не заново выяснять, что сделал Xcode.

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

Нужно ли встраивать ODR-пакеты в тестовую сборку?

Для локальной проверки это полезно: значение embedOnDemandResourcesAssetPacksInBundle=true позволяет сначала проверить состав пакетов без влияния сети. Производственный экспорт проверяют отдельно с настройками реального способа доставки.

Достаточно ли сравнить размер IPA?

Нет. Следует проверить количество файлов assetpack, соответствие тегов, корректность AssetPackManifest.plist, контрольные суммы и два запроса ресурса: до и после очистки локального кэша.

Выделенный физический узел Mac

Выберите облачный Mac, который будет постоянно доступен для следующей сборки Xcode

Проверьте чип, память, хранилище, расчётный период и регион узла, затем перейдите к настройке. Фактическая доступность отображается в консоли в реальном времени.

Выбрать конфигурацию облачного Mac