Всем привет! Это снова Эрик, инженер технической поддержки из Ринго, и мы продолжаем разговор о пакетах в экосистеме Apple. В первой части мы разобрали теорию: изучили устройство PKG-файлов, познакомились с Gatekeeper и нотаризацией, научились инспектировать пакеты с помощью встроенных утилит. Теперь перейдём к практике — будем создавать и переупаковывать пакеты.
Зачем создавать и переупаковывать пакеты
Умение собирать установочные пакеты — базовый навык системного администратора. Это необходимо не только для установки собственных утилит, но и для переупаковки стороннего программного обеспечения, чтобы установить его на компьютеры с помощью системы управления устройствами (MDM, например, Ринго).
Как мы выяснили в первой части, разработчики часто распространяют приложения в виде DMG-образов с установкой путём перетаскивания в /Applications. Такой формат неудобен для автоматизации, поскольку большинство MDM-систем работают именно с пакетами PKG. Поэтому важно понимать структуру установочного пакета и уметь собирать его самостоятельно.
Процесс развёртывания программного обеспечения обычно делится на два этапа:
- Установка приложения на клиентские устройства 
- Настройка (лицензии, параметры, подключение к серверам и т.д.) 
По возможности эти этапы стоит разделять. Это упрощает обновление и поддержку, позволяя менять конфигурацию независимо от пакета установки. Исключения возможны, но базовый принцип остаётся неизменным — разделяйте установку и настройку, а умение создавать свои пакеты и использование MDM позволяют это делать.
Теперь, когда мы разобрались с мотивацией, перейдём к простому случаю переупаковки приложений из /Applications.
Переупаковка приложений из папки /Applications
Большинство приложений в папке /Applications самодостаточны. Файл с расширением .app на самом деле является папкой (бандлом или архивом), внутри которой содержится всё необходимое для работы программы: исполняемый файл, ресурсы, библиотеки, метаданные.
Чтобы заглянуть внутрь, кликните правой кнопкой мыши по приложению и выберите «Показать содержимое пакета» (Show Package Contents). Вы увидите структуру с папкой Contents (Рис.1), где хранятся все компоненты приложения.

Приложения из Mac App Store всегда устроены именно так, но и многие сторонние программы поддерживают эту модель. Чтобы проверить, подходит ли приложение для переупаковки, проще всего попробовать это сделать.
Базовая команда для создания пакета
Для создания пакета используйте команду:
pkgbuild --component "/Applications/<AppName>.app" <AppName>-<x.y.z>.pkgЗамените <AppName> на имя приложения, а x.y.z — на его актуальную версию. Указание версии в имени пакета упрощает управление и отслеживание обновлений.
Пример для приложения Kiwix (Рис.2):
pkgbuild --component "/Applications/Kiwix.app" kiwix-3.10.1.pkg
При создании пакета с помощью утилиты pkgbuild доступны две основные опции: --component и --root.
Опция --component используется, когда исходным материалом является готовое приложение (.app). В этом случае pkgbuild автоматически извлекает идентификатор пакета и версию из метаданных приложения из файла Info.plist.
Опция --root, напротив, указывает произвольную директорию с файловой структурой пакета.
После сборки проверьте содержимое пакета. Можно воспользоваться знакомой командой из первой части:
pkgutil --expand "kiwix-3.10.1.pkg" ~/kiwix_expandedОткройте файл PackageInfo и убедитесь, что идентификатор и версия соответствуют ожидаемым значениям.
Особенности приложений из Mac App Store
При переупаковке приложений из Mac App Store есть важный нюанс. В структуре приложения присутствует файл Contents/_MASReceipt/receipt (Рис.3). Этот файл подтверждает, что программа была загружена из Mac App Store, и привязывает её к учётной записи Apple ID, использованной при покупке. Любая загрузка приложения из App Store, даже если оно бесплатное, считается покупкой.

Когда вы создаёте пакет из такого приложения, файл receipt включается в PKG. После установки пакета на других компьютерах Mac приложение будет связано с вашим Apple ID. В большинстве случаев оно будет работать, даже если было платным, однако некоторые разработчики могут использовать механизмы защиты, препятствующие подобному распространению.
Рекомендация: используйте отдельный Apple ID, если вы планируете распространять приложения из App Store, создавая пакеты и раздавая их через MDM.
Создание пакета из DMG-образа
Теперь рассмотрим более распространённый сценарий — создание пакета из DMG-образа, скачанного с сайта разработчика. В качестве примера снова возьмём Kiwix.
Вы можете смонтировать образ в Finder двойным щелчком, но давайте для расширения кругозора сделаем это в терминале (Рис.4), с помощью команды:
hdiutil attach "~/Downloads/kiwix-macos_3.10.1.dmg"
Последняя строка вывода указывает точку монтирования — в данном случае /Volumes/Kiwix-3.10.1, так как по умолчанию тома в macOS монтируются в системном каталоге /Volumes Это виртуальный том, который появится в Finder.
Просмотрите содержимое смонтированного образа (Рис.5):
ls -l "/Volumes/Kiwix-3.10.1"
Здесь мы видим:
- Само приложение Kiwix.app 
- Символическую ссылку Applications, указывающую на - /Applications
Это типичная структура DMG-образа для ручной установки методом перетаскивания.
Теперь создадим пакет (Рис.6):
pkgbuild --component "/Volumes/Kiwix-3.10.1/Kiwix.app" \
         --install-location "/Applications" \
         "kiwix-3.10.1.pkg"
# Здесь и ниже для читаемости в тексте я использую перевод строки и символ \.
Обратите внимание на параметр --install-location /Applications. Он необходим, потому что текущее расположение приложения (/Volumes/Kiwix) отличается от целевого. Этот параметр явно указывает, куда должно быть установлено приложение при запуске пакета.
Важная деталь: pkgbuild назначает владельца и группу для приложения как root:wheel(0/0). При ручной установке методом перетаскивания из DMG владельцем становится текущий пользователь (например, username:admin). Использование root:wheel предпочтительно, так как защищает приложение от изменений со стороны обычных пользователей. Однако это может повлиять на функциональность. Например, на автоматическое обновление Firefox: пользователь, запустивший приложение, не сможет изменять его содержимое.
Внимание: как упоминалось в первой части, не все DMG-образы предназначены для простого перетаскивания приложения. Перед переупаковкой обязательно проверьте содержимое DMG: откройте его в Finder и убедитесь, что внутри находится само приложение .app, а не установщик.
Работа с перемещаемыми и неперемещаемыми пакетами
По умолчанию пакеты, созданные через pkgbuild --component, являются перемещаемыми (relocatable). Это значит, что при установке пакет ищет существующую версию приложения и обновляет её на месте, даже если она установлена вне стандартного пути (например, папка с программами в домашней папке, ~/Applications вместо /Applications).
В корпоративных MDM-средах это обычно не проблема, но нестандартные расположения могут вызвать сложности.
Создание неперемещаемого пакета
Чтобы пакет устанавливался только в указанное место, используйте BundleIsRelocatable = false в component.plist:
mkdir -p "Firefox/payload"
cp -R "/Volumes/Firefox/Firefox.app" "Firefox/payload/"
pkgbuild --analyze --root "Firefox/payload" "Firefox-component.plist"
plutil -replace BundleIsRelocatable -bool NO "Firefox-component.plist"
pkgbuild \
  --root "Firefox/payload" \
  --identifier org.mozilla.firefox \
  --version 133.0 \
  --install-location "/Applications" \
  --component-plist Firefox-component.plist \
  "Firefox.pkg"Пояснение:
- --analyzeсоздаёт шаблон plist
- plutil -replaceзаменяет какое-то содержимое в файле с настройками, в данном случае отключает перемещаемость
- --component-plistприменяет изменения при сборке
Альтернативный метод
Можно отредактировать готовый пакет:
pkgutil --expand "Firefox.pkg" "Firefox_expanded"
cd "Firefox_expanded/org.mozilla.firefox.pkg"
nano PackageInfo   # заменить <relocate>...</relocate> на <relocate/>
cd ../..
pkgutil --flatten "Firefox_expanded" "Firefox-nr.pkg"Пустой тег <relocate/> делает пакет неперемещаемым (Рис.7).

Создание своего пакета
В студенческие годы я писал утилиту lsdir на Python для отображения структуры директорий. Возьму её в качестве примера. Чтобы исполнять терминальные команды без указания полного пути, нужно разместить исполняемый файл в одну из папок, указанных в переменной среды PATH. Для пользовательских системных утилит это обычно /usr/local/bin/. Поэтому удобнее распространять утилиту в виде пакета, чтобы при установке она автоматически попадала в эту директорию.
Для создания пакета необходимо создать папку с файловой структурой (Рис.8):

В данном случае папка lsdir.pkg является корнем пакета и при установке будет эквивалентна корневому каталогу (/) основной системы. То есть файлы пакета будут размещены по своим целевым путям. Например, при установке утилита lsdir окажется по пути /usr/local/bin.
Если вы хотите добавить установочные скрипты — preinstall и postinstall, — создайте папку scripts вне каталога с полезной нагрузкой (lsdir.pkg). В ней разместите соответствующие файлы. Учтите, если какой-либо из скриптов завершится с кодом 1 (возврата, exit code 1), установка будет прервана с ошибкой. Если же скрипт возвращает 0, процесс установки продолжится нормально.
Создадим пакет (Рис.9):
pkgbuild --root "lsdir.pkg" \
         --identifier com.example.lsdir \
         --version 1.0 \
         --scripts "scripts" \
         "lsdir-1.0.pkg"
Кастомный установщик с фоновым изображением и лицензией
Чтобы добавить фоновое изображение, кнопки, локализацию или лицензионное соглашение в установщик (Installer.app), необходимо собрать так называемый дистрибутивный пакет — product archive. Для этого используется утилита productbuild. В отличие от обычного .pkg, дистрибутивный пакет требует наличия управляющего файла Distribution.xml (Рис.10) и может содержать в себе несколько пакетов для установки.

Этот файл задаёт внешний вид, поведение и структуру шагов установщика macOS, включая интерфейс, фон, лицензию и подключаемые .pkg-пакеты. Подробнее о параметрах можно узнать в документации Apple.
Итак, я создал папку Resources и поместил в неё файл лицензии и фоновое изображение. У меня уже есть готовый пакет, содержащий утилиту lsdir, а также подготовленный файл Distribution.xml. Теперь можно перейти к сборке дистрибутивного пакета (Рис.11):
productbuild --distribution "Distribution.xml" \
             --resources "Resources" \
             --package-path "." \
             "lsdir.pkg"
Запустим установку и проверим, что всё на месте (Рис.12):

Мы видим фоновое изображение и текст лицензионного соглашения. Тем, кто хочет попробовать установить этот пакет или проанализировать его самостоятельно, он доступен по ссылке. К сожалению, у меня не сохранился исходный код утилиты. Поэтому если вы решите установить пакет, делаете это на свой страх и риск. Рекомендую использовать его исключительно для анализа.
Подпись и нотаризация
Как я уже упоминал в первой части статьи, чтобы macOS Gatekeeper корректно пропускал наш пакет при распространении, его необходимо подписать цифровой подписью разработчика с помощью утилиты productsign и пройти процесс нотариальной сертификации (notarization) от Apple. О том, как это сделать, можно почитать в этой статье.
Сторонние инструменты
Вы можете использовать популярные инструменты для создания пакетов, такие как Packages (графический интерфейс - Рис.13) или munkipkg. Это может быть полезно для тех, кто предпочитает GUI или интегрированные решения.

Захват файлов
В некоторых случаях приложение при первом запуске создаёт дополнительные файлы конфигурации в различных системных директориях. Для корректного создания пакета из таких приложений может потребоваться анализ изменений файловой системы. Для этого вы можете использовать Jamf Composer (Рис.14).

После этого можно собрать установочный пакет с нужной полезной нагрузкой. Подробно на этом останавливаться не будем, однако стоит отметить, что с большинством приложений подобных трудностей не возникает. Тем не менее, возможность столкнуться с необходимостью анализа изменений файловой системы всё же существует.
Заключение
Во второй части мы перешли от теории к практике и разобрали, как создавать и переупаковывать пакеты в macOS. Мы научились собирать PKG из приложений в /Applications, из DMG-образов и из собственных утилит, а также добавлять установочные скрипты и ресурсы.
 
          