Локально архив создаётся успешно, но на облачном 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, а не весь домашний каталог. При обнаружении атрибута выведите относительный путь и имя атрибута, затем завершите процесс с ненулевым кодом. Так ошибка возникнет до начала длительного архивирования, а найти связанное с ней последнее изменение будет проще.
Скрипт проверки следует хранить в системе контроля версий и запускать в фиксированном окружении:
- определять корень репозитория относительно расположения самого скрипта;
- использовать нулевые разделители для путей с пробелами;
- сохранять отчёт в отдельном каталоге артефактов текущей задачи;
- не изменять файлы во время сканирования;
- немедленно сообщать об ошибке, если целевой каталог отсутствует.
Проверьте подпись после архивирования
После успешного сканирования расширенных атрибутов проверьте архив системным средством подписи:
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. Для воспроизводимости сохраните журнал и контрольную сумму архива.
Запустите следующую задачу на выделенном физическом узле
Облачный Mac с M4, 16 ГБ ОЗУ и SSD на 256 ГБ. Выберите узел в Сингапуре, Токио, Сеуле или Гонконге под срок задачи.