Сборочная машина уже создала Example.app, а задача выпуска успешно сформировала DMG. Перед передачей пользователям нужно ответить на три вопроса: содержит ли смонтированный образ целое приложение и ссылку на папку «Программы», действительна ли подпись приложения после копирования и можно ли установить его перетаскиванием в графическом интерфейсе. Ниже эти проверки разобраны так, чтобы их можно было повторять. Замените Example.app в примерах на имя своего приложения.
Сначала определите, что именно упаковывать
На вход нужно подавать окончательную версию пакета приложения. Сначала закрепите путь к артефакту в задаче сборки, убедитесь, что исполняемый файл приложения существует, и завершите предусмотренную процедуру подписывания. Не рассматривайте создание DMG как возможность исправить содержимое приложения: изменение ресурсов, исполняемых файлов или встроенных компонентов может нарушить существующую подпись.
Проверяйте каталог командой test -d, а не только наличие одноимённого файла. Убедитесь также, что по пути вывода ещё нет старого DMG: явный сбой задачи проще расследовать, чем незаметную перезапись предыдущего артефакта выпуска. Если конвейер допускает повторные попытки, он должен заранее очищать собственный каталог вывода; скрипт упаковки не должен удалять неизвестные файлы.
Целостность образа, действительность подписи приложения и доверие системы к нему — три разных вывода. Успешная проверка одного из них не заменяет остальные.
Создайте образ со ссылкой для установки
Следующий скрипт собирает содержимое тома во временном каталоге: копирует подписанное приложение и создаёт символическую ссылку на /Applications. Для копирования пакета приложения используется ditto; после копирования не редактируйте файлы внутри пакета.
#!/bin/bash
set -euo pipefail
APP="$PWD/build/Release/Example.app"
OUT="$PWD/dist/Example.dmg"
test -d "$APP"
test -f "$APP/Contents/MacOS/Example"
mkdir -p "$(dirname "$OUT")"
test ! -e "$OUT"
work="$(mktemp -d "${TMPDIR:-/tmp}/dmg-build.XXXXXX")"
trap 'rm -rf "$work"' EXIT
stage="$work/stage"
mkdir -p "$stage"
ditto "$APP" "$stage/Example.app"
ln -s /Applications "$stage/Applications"
hdiutil create -volname "Example" \
-srcfolder "$stage" -fs HFS+ -format UDZO "$OUT"
Если исполняемый файл приложения называется не Example, измените и проверяемый путь. Имя тома — это лишь название, которое пользователь увидит после монтирования; оно не определяет путь установки приложения. Отдельные каталоги для временной сборки содержимого и итогового файла упрощают очистку при завершении скрипта и помогают не включить старый образ в новый.
Что не должно попасть на том
Сначала проверьте список файлов в каталоге сборки содержимого: там должны быть только пакет приложения и ссылка для установки. Журналы сборки, материалы для подписывания, отладочные файлы и репозиторий проекта не должны поставляться в DMG. stage в скрипте — единственный входной каталог. Если позже понадобятся файлы с инструкциями, добавьте и проверьте их явно, а не запускайте hdiutil create для всего рабочего каталога.
Смонтируйте образ заново и проверьте содержимое поставки
Недостаточно проверить исходный каталог — откройте только что созданный DMG. hdiutil verify проверяет образ; затем скрипт монтирует его в режиме только для чтения, сверяет пути на томе, копирует приложение в отдельный каталог и проверяет подпись:
#!/bin/bash
set -euo pipefail
OUT="$PWD/dist/Example.dmg"
work="$(mktemp -d "${TMPDIR:-/tmp}/dmg-check.XXXXXX")"
mountpoint="$work/mount"
attached=0
cleanup() {
if [ "$attached" -eq 1 ]; then
hdiutil detach "$mountpoint" >/dev/null || true
fi
rm -rf "$work"
}
trap cleanup EXIT
mkdir -p "$mountpoint" "$work/installed"
hdiutil verify "$OUT"
hdiutil attach -readonly -nobrowse \
-mountpoint "$mountpoint" "$OUT" >/dev/null
attached=1
test -d "$mountpoint/Example.app"
test -L "$mountpoint/Applications"
test "$(readlink "$mountpoint/Applications")" = /Applications
ditto "$mountpoint/Example.app" "$work/installed/Example.app"
codesign --verify --deep --strict \
"$work/installed/Example.app"
При ошибке скрипт завершается с ненулевым кодом, поэтому его можно включить в задачу выпуска. Если не удалось отключить том, проверьте вручную, не удерживает ли его какой-либо процесс. Не скрывайте такую проблему принудительным отключением. codesign --verify проверяет, соответствует ли скопированный пакет требованиям подписи, но не означает, что система ему доверяет. Если для распространения вне App Store предусмотрены отдельные процедуры подписывания и нотариального заверения, проверяйте результат системной оценки отдельно, согласно этим процедурам.
Пройдите путь пользователя в графическом сеансе
Командная строка позволяет проверить пути и подпись, но не доказывает, что значки расположены понятно, цель перетаскивания видна, а первый запуск проходит без ошибок. Откройте DMG в графическом сеансе macOS с выполненным входом и повторите действия обычного пользователя:
- Убедитесь, что на томе есть только ожидаемое приложение и ссылка «Программы», а их названия не обрезаны и не вводят в заблуждение.
- Перетащите приложение в «Программы» и дождитесь завершения копирования. Запуск непосредственно с тома, доступного только для чтения, не заменяет проверку установки.
- Запустите скопированное приложение, проверьте первое окно и необходимый сценарий доступа к файлам.
- Отключите том и снова запустите установленное приложение, чтобы исключить зависимость от ресурсов внутри образа.
Перед проверкой убедитесь, что в тестовом расположении нет старого приложения с тем же именем: иначе может открыться версия, установленная в прошлый раз. Перед удалением тестовой копии также сверьте путь, чтобы случайно не удалить приложение, которым пользуетесь постоянно. Если графический сеанс недоступен, отметьте выпуск как «проверки в командной строке пройдены, проверка в графическом интерфейсе не завершена», а не объединяйте эти результаты в общий успех.
Ищите причину сбоя на соответствующем этапе
Если hdiutil verify завершается с ошибкой, сначала сохраните исходный DMG и журнал его создания. Проверьте, не начали ли загружать или читать файл до завершения записи. Если на томе не хватает файлов, проверьте каталог сборки содержимого и входной путь для ditto. При ошибке проверки подписи выясните, не менял ли какой-либо скрипт пакет приложения после подписывания, и создайте образ заново из окончательной версии приложения. Повторные точечные исправления существующего DMG затрудняют отслеживание того, что именно было передано пользователям.
Если все проверки в командной строке проходят, но приложение не запускается после перетаскивания, в первую очередь проверьте фактический путь запуска, подпись приложения и результат системной проверки доверия, необходимой для внешнего распространения. В завершение запишите идентификатор сборки приложения, контрольную сумму файла DMG, результаты автоматических проверок и вывод графической проверки. Это поможет при следующем выпуске определить, на каком этапе возникла проблема: сборки, упаковки или установки.
Часто задаваемые вопросы
Гарантирует ли hdiutil verify запуск приложения?
Нет. Команда проверяет образ; подпись приложения и его запуск после копирования проверяются отдельно.
Когда подписывать приложение?
До создания DMG. Подпишите окончательный пакет приложения и проверьте его; после изменений соберите образ заново.
Нужен выделенный Mac в облаке?
Выберите модель, локацию и срок оплаты. Вам будет предоставлен выделенный физический сервер; фактическая доступность отображается в консоли в реальном времени.