Краткое резюме: подпись — это семантика ФС, а не только байты
Современные конвейеры Apple рассматривают подписанный .app как структурированный каталог с вложенным кодом, ресурсами и метаданными, которые должны оставаться согласованными. Инструменты, воспринимающие бандл как свободный набор файлов, могут скопировать каждый байт и всё же разрушить граф, который проверяет codesign. Симптом у пользователей — сообщения Gatekeeper, звучащие как про вредоносное ПО, хотя проблема в гигиене упаковки.
Команды, централизующие сборки на удалённом Mac, часто сначала оптимизируют компиляторные фермы и позже обнаруживают, что движение артефактов прячет регрессии. Исправление — не только более агрессивное хеширование. Нужно сочетать криптографические манифесты с проверкой подписи после каждого хопа: tarball может сохранить байты, пока распаковка переписывает права.
Однородный rsync macOS сохраняет POSIX-права и многие расширенные атрибуты при явных флагах, что удобно между двумя build-агентами. Как только в цепочке появляется Linux-runner, по умолчанию нужен tar.gz на macOS, чтобы цели symlink оставались членами архива, а не преждевременно раскрывались. Не предполагайте, что upload-artifact в GitHub Actions совпадает с перетаскиванием в Finder; закрепите major-версию, потому что обработка symlink меняется. Если ревьюеры спорят, сначала tarball и один blob вверх, чтобы downstream не переосмысливал дерево.
Семантика релиза неизменна: писатели попадают в releases/<build_id>/, автоматизация продвигает только после манифестов, публичные symlink меняются атомарно. Работа с подписью дополняет дисциплину, а не заменяет её. Если узкое место — пропускная способность раньше подписей, сначала прочитайте WAN-гайд, затем вернитесь сюда.
Комплаенс всё чаще спрашивает, какая машина касалась ключей подписи и какая экспортировала клиентские бинарники. Фиксируйте идентификаторы отправки notarytool вместе с git SHA и номерами сборки Xcode, чтобы аудит через полгода оставался читаемым.
Ранбуки должны указывать, кто может запускать ad-hoc ditto или cp -R на общих шарах. Неформальные копии обходят tar-дисциплину, сохраняющую symlink. Если маркетинг просит last-minute zip-демо, направьте через ту же автоматизацию, что и беты, чтобы проверка осталась на артефакте.
Обучите поддержку: ошибки Gatekeeper после скачивания могут быть регрессией упаковки, а не ИБ-инцидентом. Первая линия может локально выполнить verify-команды до эскалации и сэкономить время смежным командам.
Пять классов тихих поломок
Сплющенные бандлы. Графические zip-утилиты и часть облачных синхронизаторов пересобирают каталоги без macOS-метаданных. Визуально всё цело, глубокая проверка падает.
Linux-посредник. Потоки, грузящие каталоги с macOS-runner на Linux, часто ломают подписи при разыменовании symlink или невыразимых правах. tar не разыменовывает, пока вы явно не попросите.
In-place rsync на живые деревья. --inplace на путях, доступных пользователю во время передачи, показывает частичные бандлы. Сочетайте rsync со staging-префиксами и атомарной сменой указателей из статьи про атомарный релиз.
Дрейф stapler. Команды stapлят dmg, затем пережимают. Билет нотаризации больше не соответствует байтам у пользователей. Считайте stapнутые артефакты неизменяемыми без полного цикла нотаризации.
Дыры проверки. Проверять только на исходном Mac экономит минуты сегодня и стоит часов в инциденте. Повторяйте codesign --verify --deep --strict на Mac, который отдаёт сборки людям.
Смешение ролей runner. Если PR-воркфлоу делит upload-пути с релизом, поддеревья могут перезаписаться до реакции манифестов. Разделяйте учётки и POSIX-права согласно CI-матрице.
Перекодирование в объектном хранилище. Некоторые пайплайны кладут dmg в бакеты с серверным перекодированием, меняя байты без локального шага. Документируйте immutability и версионируемые ключи на сборку.
Матрица: сначала упаковка, потом трюки с пропускной способностью
Собирайте в одном тикете build id, signing identity, submission id нотаризации, версии упаковщика, транспорта и коды выхода verify, чтобы не ловить split-brain при разборе.
| Топология | Риск | Предпочтительный перенос | Связанная статья |
|---|---|---|---|
| Mac к Mac низкая задержка | низкий при явных флагах | rsync с сохранением метаданных | Параллельный SFTP |
| Mac к Linux к Mac | высокий | один tar.gz на сборку | Шлюз целостности |
| публичный dmg | средний после staple | заморозить имя после staple | Атомарный релиз |
| внутренний кэш | контролируемый | промежуточные неподписанные ок, если promotion пересобирает цепочку | CI-матрица |
| крупные полезные нагрузки WAN | таймауты | сначала tar, потом настраивать параллель | WAN-матрица |
Оптимизации скорости никогда не отменяют правило: читатели видят только полные деревья под стабильными указателями.
Фиксируйте ожидаемые минуты на шаг verify, чтобы планировать людей, ожидающих нотаризацию, а не только кремний, компилирующий исходники. Снимайте медиану и p95 еженедельно, чтобы регрессии упаковщиков всплыли раньше клиентов.
Скелет staging-скрипта
Замените хосты и пользователей. Секреты согласуйте с CI-матрицей.
# На сборочном Mac: архив до Linux-хопа
COPYFILE_DISABLE=1 tar -czvf MyApp_build.tar.gz MyApp.app
codesign --verify --deep --strict --verbose=2 MyApp.app
rsync -av MyApp_build.tar.gz ci@remote-mac:uploads/staging/build-9001/
# На удалённом Mac: распаковка и verify приземления
ssh ci@remote-mac 'cd uploads/staging/build-9001 && tar -xzvf MyApp_build.tar.gz'
ssh ci@remote-mac 'codesign --verify --deep --strict uploads/staging/build-9001/MyApp.app'
# Продвигать только после прохождения манифеста (атомарный релиз)
Интерактивным SFTP нужны письменные SOP по переключателям прав. Один неверный дефолт губит ночную бету. Добавьте скриншоты в хендбук, чтобы дежурство не гадало.
Поля тикета, очереди нотаризации и свободное место
Минимум: git SHA, версия Xcode, патч macOS, сводка identity codesign, submission id notarytool, состояние stapler, коды verify источника и назначения, версии транспортных утилит, id манифеста SHA256. Команды, логирующие только семантические версии, снова находят загадки в дедлайн-недели App Store.
Очереди нотаризации во время платформенных событий Apple легко тянутся дольше тридцати минут. Бюджетируйте настенное время как фермы компиляции. Диски удалённых Mac, долго оставшиеся ниже ~120 ГБ свободных, провоцируют трудновоспроизводимые сбои архивации; сочетайте cron-уборку и алерты мониторинга.
Разделяйте учётки загрузки для подписанных релизных деревьев и креативных ассетов. Случайные перезаписи проще предотвратить, когда POSIX-права и SSH-ключи различаются по ролям. Продвинутые chroot-схемы описаны в других материалах сайта; суть — сильнее изолировать материал подписи.
Если legal спрашивает, включает ли загрузка нотаризации исходники, уточните, что Apple обрабатывает бинарники и метаданные. Тем не менее относитесь к бандлам как к конфиденциальным клиентским артефактам в объектном хранилище с путями least privilege.
Ротируйте креды подписи в том же ритме, что deploy keys CI, и не смешивайте прод-хост подписи с неревьюными PR-воркфлоу.
Выделяйте отдельные стадии CI для stapler и spctl, чтобы параллельные джобы не читали-писали одну dmg. Сериализуйте promotion, если инструменты ждут файловых блокировок.
Версионируйте кэши runner; гигиена DerivedData влияет на совпадение локальных проверок с чистым архивируемым деревом.
Параллельные загрузки независимых tarball безопасны, пока у каждого писателя свой префикс build_id и никто не трогает один публичный symlink. Если оркестратор шлёт ретраи без джиттера, частично записанные файлы всё равно множатся — добавьте backoff и staging из гайда по атомарному релизу.
CGNAT и длинные SFTP-сессии рвутся и подталкивают клиентов к ресинхронизации на уровне каталога, что даёт частичные распаковки. Документируйте флаги возобновления и после каждого обрыва проверяйте полностью, не выборочно.
Долг наблюдаемости возникает, когда коды выхода verify оседают только в stdout без структурных полей CI. JSON-строки по хопам укорачивают постмортемы и помогают саппорту отличить реальный разрыв подписи от сетевого таймаута.
Корпоративные прокси с глубокой инспекцией TLS обычно не расшифровывают SSH, но эвристики по интервалам пакетов всё равно могут рвать «тихие» фазы rsync, когда полезная нагрузка редка. Логируйте фазы handshake, время до первого байта и длительность сканирования, иначе инциденты ошибочно спишут на «битый диск» удалённого Mac.
Кэш артефактов внутри VPC иногда переупаковывает объекты для дедупликации. Если ваш провайдер делает это прозрачно, убедитесь, что ключи объектов immutable и что манифест SHA256 считается по тому же потоку байтов, который увидит конечный Mac, а не по промежуточному представлению в бакете.
Обучение онбординга для новых разработчиков должно включать пять минут про «не копировать .app через произвольный GUI-клиент». Это дешевле, чем ночной разбор, почему внутренняя сборка «внезапно» перестала проходить Gatekeeper у дизайнеров.
Глоссарий для точных постмортемов
Application bundle — каталожная конвенция для исполняемых файлов, фреймворков и ресурсов; это не один файл, поэтому наивное копирование молча ломается.
Developer ID Application подписывает бинарники для распространения вне Mac App Store с ожиданиями нотаризации Apple.
Билет нотаризации — запись Apple об успешных сканах и политиках; stapler встраивает доказательство для офлайн-проверки.
codesign verify deep strict обходит вложенный код и требует согласованности, которую рыхлые копии часто нарушают.
Сохранение symlink означает, что относительные ссылки после транспорта всё ещё указывают на нужные вложенные хелперы.
Расширенные атрибуты несут resource fork и карантинные метки; не-macOS ФС часто выбрасывают их целиком.
tar copyfile disable помогает исключить AppleDouble sidecar для переносимых архивов.
Сплющивание артефакта — инструменты, превращающие symlink в дубликаты файлов или отбрасывающие исполняемые POSIX-биты.
Атомарный указатель релиза — symlink или эквивалент, который меняют только после завершения проверки.
Манифест хешей — список криптографических сумм, утверждённый людьми независимо от внутренних проверок инструментов.
Staging-префикс — изолированное пространство записи от клиентских деревьев загрузки.
Идентичность runner CI — пользователь ОС, выполняющий шаги; смешение signing identity без изоляции ведёт к утечкам ключей.
Gatekeeper — политика macOS, учитывающая нотаризацию и подпись перед запуском скачанных приложений.
Hardened runtime должен соответствовать реальному доступу к файлам; нотаризация не чинит несовпадающие entitlements.
Stapler validate проверяет соответствие встроенных билетов байтам на диске для поддерживаемых типов пакетов.
Ферма удалённых Mac централизует совместимость Apple Silicon и цепочки подписи для распределённых команд.
Сессия SFTP переносит файлы внутри SSH, наследуя шифрование и аутентификацию.
Инкрементальный алгоритм rsync экономит полосу на повторных синхронизациях, но может быть расточительным для свежих полных копий гигантских одиночных файлов.
upload-artifact v4 ведёт себя иначе, чем сырой tar, по symlink; всегда проверяйте после скачивания на macOS.
Immutability объектного хранилища предотвращает подмену после загрузки при версионируемых ключах на сборку.
Blast radius измеряет, сколько клиентов получит сломанную сборку при слишком раннем promotion.
Профиль provisioning связывает entitlements с team id; несоответствия всплывают при подписи даже при идеальной сети.
Гигиена DerivedData важна: устаревшие промежутки дают бинарники, которые локально проходят verify, но расходятся после чистого архива.
xcarchive собирает dSYM и продукты для дистрибуционных пайплайнов; трактуйте как отдельный тип архива с явными правилами распаковки.
Оценка spctl дополняет codesign при проверке готовности к распространению.
Карантинный атрибут помечает загрузки из интернета; автоматизация должна понимать, когда его снятие уместно для CI-фикстур.
Партиция связки ключей разделяет идентичности подписи по сессиям на общих хостах; документируйте разблокировку для безлюдных сборок.
Метрики time-to-verify должны стоять рядом с длительностью компиляции, чтобы руководство видело полную задержку релиза.
Rollback-артефакт — предыдущая промотированная сборка для мгновенного отката symlink при регрессии подписи.
Поле наблюдаемости фиксирует структурированный JSON с кодами verify по хопам.
Региональный egress влияет на задержку нотаризации, если endpoint Apple различаются географически; логируйте uplink на сборку.
Хеш-шлюз в смысле гайда по целостности намеренно независим от кода выхода rsync или SFTP-клиентов.
Таксономия инцидентов с тегами flatten, linux_hop, inplace, staple_drift, verify_gap ускоряет запросы к логам.
Арендованный удалённый Mac объединяет железо, egress и шаблоны sshd, чтобы продуктовые команды двигались вперёд, а не заново открывали сплющивание symlink.
FAQ и зачем брать хостинг Mac
Можно ли валидировать подписи на Linux?
Не делайте Linux авторитетом для графа подписи Apple. Linux может нести tarball, проверка — на macOS.
ZIP всегда плох?
Не автоматически, но дефолтные кодеры различаются. Если нужен ZIP, документируйте флаги и каждый раз проверяйте на целевом Mac.
Помогает ли rclone подписям?
rclone силён в зеркалах и multi-backend sync. Считайте его ещё одним транспортом с явными флагами метаданных, не магией.
Итог: Сохраняйте семантику бандла, tar до Linux, проверяйте на каждом хопе, держите релизы в staging, пока манифесты не согласны.
Предел DIY: Самодельный мультирегиональный ingress умножает ранбуки и дежурства. Арендованные удалённые Mac SFTPMAC собирают стабильные сборочные хосты, шифрованные пути загрузки и политики сессий, чтобы инженеры фокусировались на качестве продукта, а не на повторном открытии сплющенных symlink.
Посмотрите планы и регионы SFTPMAC, чтобы выровнять хосты подписи и ingress доставки.
