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

Как найти сбой подписи iOS из-за расширенных атрибутов

Как найти сбой подписи iOS из-за расширенных атрибутов

Локально архив создаётся успешно, но на облачном Mac на этапе подписи возникает ошибка resource fork, Finder information, or similar detritus not allowed. Обычно причина не в истёкшем сертификате или профиле подготовки, а в расширенных атрибутах macOS, которые сохранились у изображения, результата работы скрипта или скопированного каталога. Вместе с ресурсами эти метаданные попадают в пакет App и обнаруживаются только тогда, когда средство подписи запускает строгую проверку.

Сначала определите, на каком уровне происходит сбой

Не пересоздавайте сертификаты сразу после сообщения об ошибке подписи. Сначала найдите первую ошибку codesign в полном журнале сборки и определите, к какому объекту она относится: основному App, Framework, расширению или пакету ресурсов. Если в журнале явно указаны FinderInfo, ResourceFork или detritus, дальнейшую диагностику следует сосредоточить на метаданных файлов.

Расширенные атрибуты часто встречаются в следующих местах:

  • в изображениях, шрифтах и архивах, скопированных в репозиторий через графический файловый менеджер;
  • в ресурсах, которые распаковали из общего каталога и сразу добавили в репозиторий;
  • в заранее подготовленных каталогах, перенесённых скриптом сборки с помощью cp -R;
  • в локальных результатах, которые не отслеживаются системой контроля версий, но долго хранятся в рабочем каталоге;
  • в зависимостях, восстановленных из кеша вместе с файловыми метаданными.

Удаление DerivedData очищает только результаты сборки. Если загрязнение находится в исходном коде или во внешних входных данных, следующая сборка стабильно воспроизведёт ту же ошибку.

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

Создайте исходную точку с проверкой только для чтения

Проверьте входные файлы репозитория

Команда xattr -lr рекурсивно выводит пути и связанные с ними атрибуты. Сначала запишите результаты в журнал и не удаляйте атрибуты во время сканирования:

set -euo pipefail

ROOT="${SRCROOT:-$PWD}"
REPORT="${TMPDIR:-/tmp}/ios-xattr-source.log"

xattr -lr "$ROOT" > "$REPORT" 2>&1 || true

grep -E \
  'com\.apple\.(FinderInfo|ResourceFork)' \
  "$REPORT" || true

Если репозиторий большой, можно исключить .git, DerivedData и кеши зависимостей, а затем отдельно проверить каталоги, которые действительно участвуют в упаковке. Важно не количество совпадений, а ответы на три вопроса: к какому файлу прикреплён атрибут, каким процессом создан этот файл и через какую Build Phase он попадает в пакет?

Место обнаружения Что проверить в первую очередь Принцип устранения
Assets или каталог ресурсов Способ импорта файлов, инструмент распаковки Исправить исходный файл
Каталог вывода скрипта Параметры cp и ditto Пересоздать промежуточный каталог
DerivedData Восстановление кеша, старые результаты Очистить кеш и воспроизвести
Внутри Framework Этапы загрузки и распаковки Загрузить заново и проверить

Проверьте архивированный пакет

Если архив уже создан, просканируйте непосредственно .app, чтобы убедиться, что загрязнение попало в итоговый пакет:

ARCHIVE_PATH="$PWD/build/App.xcarchive"
APP_PATH="$ARCHIVE_PATH/Products/Applications/App.app"
REPORT="$PWD/build/xattr-app.log"

test -d "$APP_PATH"
xattr -lr "$APP_PATH" > "$REPORT" 2>&1 || true

if grep -Eq 'com\.apple\.(FinderInfo|ResourceFork)' "$REPORT"; then
  echo "forbidden extended attributes found"
  exit 1
fi

Здесь для примера используется фиксированный путь. В реальном конвейере следует определить единственный .app в каталоге архива и немедленно завершить задачу с ошибкой, если не найдено ни одного кандидата или найдено несколько. Это предотвращает проверку не того пакета.

Сохраняйте прослеживаемость при очистке

Не рекомендуется сразу выполнять xattr -cr для всего рабочего пространства. Эта команда удаляет все расширенные атрибуты, неоправданно увеличивает область изменений и может скрыть проблему на этапе загрузки, распаковки или восстановления кеша. Надёжнее удалять только два типа атрибутов, которые гарантированно нарушают подпись:

TARGET="$PWD/StagingPayload"

find "$TARGET" -print0 |
while IFS= read -r -d '' item; do
  xattr -d com.apple.FinderInfo "$item" 2>/dev/null || true
  xattr -d com.apple.ResourceFork "$item" 2>/dev/null || true
done

Лучше, чтобы TARGET указывал на одноразовую промежуточную копию, а не на исходное рабочее пространство разработчика. После очистки выполните повторное сканирование. Если совпадения остались, остановите сборку и не добавляйте || true к условию итоговой приёмки.

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

Встройте проверку в процесс архивирования

Проверяйте входные данные до сборки

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

Скрипт проверки следует хранить в системе контроля версий и запускать в фиксированном окружении:

  1. определять корень репозитория относительно расположения самого скрипта;
  2. использовать нулевые разделители для путей с пробелами;
  3. сохранять отчёт в отдельном каталоге артефактов текущей задачи;
  4. не изменять файлы во время сканирования;
  5. немедленно сообщать об ошибке, если целевой каталог отсутствует.

Проверьте подпись после архивирования

После успешного сканирования расширенных атрибутов проверьте архив системным средством подписи:

APP_PATH="$PWD/build/App.xcarchive/Products/Applications/App.app"

codesign --verify \
  --strict \
  --verbose=2 \
  "$APP_PATH"

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

Завершите диагностику приёмочным списком

Надёжное исправление должно одновременно соответствовать следующим условиям:

  • результаты сканирования исходного кода и внешних входных данных сохранены;
  • определён процесс создания или копирования загрязнённого файла;
  • очистка затрагивает только явно установленные атрибуты или одноразовую промежуточную копию;
  • после удаления DerivedData и связанных кешей архив можно создать заново;
  • в итоговом пакете App отсутствуют FinderInfo и ResourceFork;
  • основной App и вложенные объекты кода проходят строгую проверку подписи;
  • один и тот же коммит стабильно проходит проверку в новом рабочем каталоге.

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

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

Почему удаление DerivedData не устраняет ошибку подписи?

Если FinderInfo или ResourceFork находится в исходных ресурсах, загруженных файлах либо каталогах, которые копирует скрипт, новая сборка снова перенесёт атрибут в пакет. Сначала проверьте входные данные.

Безопасно ли запускать xattr -cr для всего репозитория?

Это слишком широкая очистка. Сначала сохраните список совпадений и удаляйте только FinderInfo и ResourceFork. Полную рекурсивную очистку выполняйте лишь на временной копии.

Как проверить архив после исправления?

Убедитесь, что в App-пакете нет целевых атрибутов, затем выполните codesign --verify --strict. Для воспроизводимости сохраните журнал и контрольную сумму архива.

NowMini M4

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

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

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