После слияния вполне обычной функции размер архива неожиданно вырос более чем на десяток мегабайт, хотя в истории коммитов не было ни крупных изображений, ни явно добавленных зависимостей. Если проверять IPA вручную только перед выпуском, к этому моменту проблема обычно уже успевает попасть в несколько веток. Надёжнее хранить базовые значения размера в фиксированной среде сборки на облачном Mac, после каждого архивирования автоматически измерять отдельно App, основной исполняемый файл и динамические фреймворки, а при превышении порога блокировать слияние.
Сначала определите сопоставимые объекты измерения
У понятия «размер сборки» есть как минимум три разных значения, и их нельзя смешивать на одном графике.
| Метрика | Объект измерения | Основное назначение |
|---|---|---|
| Размер App в распакованном виде | Каталог .app внутри .xcarchive |
Выявление роста ресурсов, фреймворков и файлов локализации |
| Размер основного исполняемого файла | Файл Mach-O, указанный в CFBundleExecutable |
Выявление изменений в коде, статических библиотеках и символах |
| Размер IPA | Экспортированный сжатый файл | Наблюдение за размером пакета, который фактически загружают пользователи |
Основными метриками для гейта должны быть первые две. Размер IPA зависит от степени сжатия и порядка файлов, поэтому даже небольшие изменения в одном и том же наборе ресурсов могут заметно увеличить или уменьшить итоговый сжатый файл. Отладочные символы находятся в каталоге dSYMs архива и не должны учитываться в размере устанавливаемого пользователем пакета. При этом их следует сохранять отдельно для последующего анализа проблем.
Разница в размере имеет смысл только при одинаковых условиях сборки. Необходимо зафиксировать путь к Xcode, конфигурацию, целевую платформу, способ экспорта и параметры компиляции.
Создавайте архив фиксированной командой
Сначала разрешите зависимости в чистом рабочем каталоге, а затем создайте архив Release для реального устройства. Имя рабочей области и Scheme передаются через параметры задания, чтобы не фиксировать сведения о конкретном проекте внутри скрипта.
set -euo pipefail
WORKSPACE="${WORKSPACE:?missing WORKSPACE}"
SCHEME="${SCHEME:?missing SCHEME}"
OUT="${OUT:-$PWD/build-size}"
ARCHIVE="$OUT/App.xcarchive"
rm -rf "$OUT"
mkdir -p "$OUT"
xcodebuild \
-workspace "$WORKSPACE" \
-scheme "$SCHEME" \
-configuration Release \
-destination 'generic/platform=iOS' \
-archivePath "$ARCHIVE" \
clean archive \
CODE_SIGNING_ALLOWED=NO
Если на этапе архивирования проекту необходимо выполнять скрипты, связанные с подписью, не следует принудительно отключать её. Используйте штатную контролируемую конфигурацию проекта. Задача гейта — обеспечить условия, совпадающие с производственной сборкой, а не создать одну универсальную команду для всех репозиториев. При первоначальном внедрении также убедитесь, что в конфигурацию Release по ошибке не попали тестовые ресурсы, диагностические библиотеки или адрес сервера разработки.
Измеряйте App, исполняемый файл и фреймворки отдельно
Приведённый ниже скрипт находит единственный App в архиве, считывает имя основного исполняемого файла и выводит TSV, пригодный для машинного разбора. Команда du -sk удобна для регулярного сравнения места, занимаемого каталогом, а stat позволяет получить точный размер основного исполняемого файла в байтах.
set -euo pipefail
ARCHIVE="${1:?usage: measure.sh path/to/App.xcarchive}"
APP_ROOT="$ARCHIVE/Products/Applications"
APP_PATH="$(find "$APP_ROOT" -maxdepth 1 -type d -name '*.app' -print -quit)"
test -n "$APP_PATH"
EXECUTABLE="$(/usr/libexec/PlistBuddy \
-c 'Print :CFBundleExecutable' "$APP_PATH/Info.plist")"
printf "kind name bytes
"
APP_KB="$(du -sk "$APP_PATH" | awk '{print $1}')"
printf "app %s %s
" "$(basename "$APP_PATH")" "$((APP_KB * 1024))"
printf "executable %s %s
" "$EXECUTABLE" \
"$(stat -f '%z' "$APP_PATH/$EXECUTABLE")"
FRAMEWORKS="$APP_PATH/Frameworks"
if test -d "$FRAMEWORKS"; then
find "$FRAMEWORKS" -maxdepth 1 -type d -name '*.framework' -print0 |
while IFS= read -r -d '' item; do
kb="$(du -sk "$item" | awk '{print $1}')"
printf "framework %s %s
" "$(basename "$item")" "$((kb * 1024))"
done
fi
Отчёт нужно сохранять как артефакт текущей сборки, а не ограничиваться выводом одного общего значения. При возникновении регрессии рецензент сможет сразу определить, связан ли рост с основным исполняемым файлом, отдельным фреймворком или ресурсами во всём каталоге App.
Детализируйте каталоги ресурсов
Если общий размер App увеличился, а основной исполняемый файл и фреймворки остались стабильными, можно отдельно применить stat или du к Assets.car, каталогам локализации, файлам автономных данных и медиаресурсам. Не удаляйте файлы только потому, что они кажутся дубликатами. Сначала убедитесь, что они не создаются разными target, правилами ресурсов по требованию или процессом локализации.
Сократите число ложных срабатываний с помощью двойного порога
Если использовать только процентное значение, небольшие компоненты становятся чрезмерно чувствительными. Если учитывать только фиксированное число байтов, можно пропустить постоянный рост крупного App. Допустимое увеличение можно определить так:
allowed = max(8 MiB, baseline_app_bytes × 3%)
failed = current_app_bytes - baseline_app_bytes > allowed
Значения 8 MiB и 3% здесь являются лишь начальными параметрами внедрения, а не универсальным стандартом. Сначала зафиксируйте колебания нескольких обычных архивов, а затем настройте значения под конкретный проект. Для основного исполняемого файла и каждого отдельного фреймворка следует использовать меньшие независимые пороги, иначе новая зависимость может затеряться в общем размере App.
Файл базовых значений должен проходить ревью вместе с исходным кодом, но задание, завершившееся ошибкой, не должно автоматически перезаписывать его. Корректный процесс выглядит так: задание выводит разницу, разработчик объясняет её причину, рецензент подтверждает соответствие изменения требованиям, после чего базовые значения обновляются в рамках того же изменения. Это позволяет отличить осознанно одобренный рост от сброса чисел ради прохождения проверки.
Разбирайте типичные причины аномального роста
Если увеличился основной исполняемый файл, сначала проверьте статические зависимости, инстанцирование обобщённых типов, повторное связывание и условия компиляции. При росте фреймворка проверьте версию зависимости и способ её встраивания. Если выросли ресурсы, изучите исходные изображения, шрифты, аудио- и видеофайлы, а также дублирующийся локализованный контент.
Следует также избегать четырёх распространённых ошибок:
- Напрямую сравнивать результаты, созданные в разных средах Xcode.
- Смешивать сборки для симулятора с архивами для реальных устройств.
- Сохранять только общий размер без детализации компонентов и идентификатора коммита.
- Автоматически повышать порог или перезаписывать базовые значения после срабатывания гейта.
Итоговый отчёт должен как минимум содержать идентификатор коммита, конфигурацию архива, общий размер App, размер основного исполняемого файла, несколько самых крупных фреймворков, прирост относительно базового значения и результат проверки. Отчёты следует хранить и после успешного прохождения гейта, чтобы отслеживать медленный, но непрерывный рост. При срабатывании гейта нужно сохранить архив и подробные данные, а затем повторно проверить их в той же среде, не полагаясь на локальную пересборку разработчика.
Часто задаваемые вопросы
Почему недостаточно сравнивать только размер готового IPA?
IPA является сжатым архивом, поэтому результат зависит от содержимого ресурсов, порядка файлов и поведения архиватора. Основными метриками лучше сделать несжатый каталог приложения и исполняемый файл.
Как выбрать порог регрессии размера приложения?
Сначала соберите данные нескольких штатных выпусков, затем применяйте одновременно абсолютный и относительный пороги. Значения 8 MiB и 3% из примера следует адаптировать к истории конкретного проекта.
Можно ли сразу обновить базовую линию после обновления зависимости?
Сначала проверьте, какие символы, ресурсы или фреймворки добавились и соответствуют ли они ожидаемому изменению. Базовую линию обновляют только вместе с объяснением и проверенным изменением.
Разместите следующую сборку iOS на MiniDebug M4.
В стандартную комплектацию входят M4, 16 ГБ ОЗУ и SSD на 256 ГБ. Доступна аренда на день, неделю, месяц или квартал, а выбрать можно один из пяти узлов: Сингапур, Токио, Южная Корея (Сеул), Гонконг или восток США. Фактическая доступность отображается в консоли в реальном времени.