Сборка и проверка macOS PKG в облачном Mac

DevOps и CI/CD ·~5 мин чтения

Сборка и проверка macOS PKG в облачном Mac

Когда команде нужно развернуть утилиту командной строки, фоновый агент или внутреннее корпоративное приложение на нескольких компьютерах Mac, простое копирование файлов быстро приводит к трём проблемам: целевые пути различаются, после обновления остаются старые файлы, а результат установки невозможно проверить в рамках аудита. Формат macOS PKG позволяет объединить payload, права доступа, установочные сценарии и квитанцию с версией в единый отслеживаемый артефакт. Однако для этого нельзя просто передавать рабочий каталог разработчика в pkgbuild. Ниже на примере компонента командной строки, устанавливаемого в /Library/Application Support/AcmeTool, показан процесс сборки и приёмочного тестирования, пригодный для CI на облачных Mac.

Сначала определите контракт установки

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

Параметр Пример Критерий приёмки
Корневой каталог установки /Library/Application Support/AcmeTool Не записывать данные в домашний каталог пользователя
Исполняемый файл bin/acmetool root:wheel, права 0755
Шаблон конфигурации config/default.json Права 0644, без секретов
Идентификатор пакета com.example.acmetool.pkg Не меняется в зависимости от ветки
Версия 1.4.0 Совпадает с тегом релиза

Каталог проекта на сборочной машине не является частью контракта установки. В PKG не должны попадать .git, отчёты тестирования, кеш загрузок, временные журналы и расширенные атрибуты, созданные на локальной машине.

Самый надёжный источник для упаковки — не «текущее содержимое репозитория», а заново созданный staging-каталог с полностью перечислимым содержимым.

Создайте чистый payload

В начале каждой задачи удаляйте прежнюю staging-область, явно копируйте только разрешённые к поставке файлы, а затем централизованно назначайте права. Не используйте слишком широкий вызов cp -R .: из-за него новые файлы могут незаметно попасть в установочный пакет.

set -euo pipefail

ROOT="$PWD"
STAGE="$ROOT/build/stage"
PKGDIR="$ROOT/build/packages"
INSTALL_DIR="$STAGE/Library/Application Support/AcmeTool"

rm -rf "$STAGE" "$PKGDIR"
mkdir -p "$INSTALL_DIR/bin" "$INSTALL_DIR/config" "$PKGDIR"

install -m 0755 "$ROOT/dist/acmetool" "$INSTALL_DIR/bin/acmetool"
install -m 0644 "$ROOT/packaging/default.json" \
  "$INSTALL_DIR/config/default.json"

xattr -cr "$STAGE"
find "$STAGE" -type d -exec chmod 0755 {} +
find "$STAGE" -type f ! -path '*/bin/acmetool' -exec chmod 0644 {} +

Затем создайте манифест: он пригодится и при проверке кода, и для сравнения с фактическими входными данными после завершения сборки.

find "$STAGE" -print0 |
  sort -z |
  xargs -0 stat -f '%Sp %Su:%Sg %N' > "$PKGDIR/payload-manifest.txt"

grep -Eq '/(\.git|DerivedData|node_modules)(/|$)' \
  "$PKGDIR/payload-manifest.txt" && exit 1 || true

Здесь сначала удаляются расширенные атрибуты, а затем назначаются права. Это предотвращает попадание в артефакт меток источника загрузки или ACL с машины разработчика. В шаблоне конфигурации допустимы только значения по умолчанию; токены среды выполнения следует внедрять при развёртывании.

Соберите пакет компонента с помощью pkgbuild

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

mkdir -p packaging/scripts
cat > packaging/scripts/postinstall <<'SH'
#!/bin/sh
set -eu

TARGET="/Library/Application Support/AcmeTool"
test -x "$TARGET/bin/acmetool"
chown -R root:wheel "$TARGET"
chmod 0755 "$TARGET" "$TARGET/bin" "$TARGET/bin/acmetool"
chmod 0644 "$TARGET/config/default.json"
exit 0
SH
chmod 0755 packaging/scripts/postinstall

pkgbuild \
  --root "$STAGE" \
  --identifier "com.example.acmetool.pkg" \
  --version "1.4.0" \
  --install-location "/" \
  --scripts packaging/scripts \
  "$PKGDIR/AcmeTool-component.pkg"

Если компонент только один, пакет компонента уже можно устанавливать. Если нужны единая страница приветствия, текст лицензии или объединение нескольких компонентов, создайте дистрибутивный пакет с помощью productbuild:

productbuild \
  --package "$PKGDIR/AcmeTool-component.pkg" \
  "$PKGDIR/AcmeTool-1.4.0.pkg"

Если требуется подпись, считывайте идентификатор подписи Installer в защищённой среде CI и собирайте официальный пакет с параметром --sign "$INSTALLER_IDENTITY". Неподписанные артефакты можно использовать для предварительного тестирования, но нельзя выдавать за окончательный поставляемый пакет.

Выполните статическую проверку до установки

Первый уровень проверки не изменяет систему. Сначала проверьте идентификатор пакета, версию и payload, затем распакуйте дистрибутивный пакет и проверьте сценарии и метаданные:

PKG="$PKGDIR/AcmeTool-1.4.0.pkg"
EXPANDED="$PKGDIR/expanded"

pkgutil --check-signature "$PKG" || true
pkgutil --payload-files "$PKGDIR/AcmeTool-component.pkg"
rm -rf "$EXPANDED"
pkgutil --expand-full "$PKG" "$EXPANDED"

grep -R "com.example.acmetool.pkg" "$EXPANDED"
grep -R "1.4.0" "$EXPANDED"
find "$EXPANDED" -type f -name postinstall -exec sh -n {} \;

Если официальный пакет обязан быть подписан, не допускайте продолжения после ошибки pkgutil --check-signature: ненулевой код завершения должен немедленно останавливать конвейер. Также необходимо отклонять абсолютные пути сборки, фрагменты закрытых ключей и имена пользователей из рабочей среды:

if grep -R -E "$HOME|BEGIN (RSA |EC )?PRIVATE KEY" "$EXPANDED"; then
  echo "sensitive build data found" >&2
  exit 1
fi

Распространённая ошибка — сравнивать только размер файла пакета. Стабильный размер не означает стабильное содержимое. Манифест, идентификатор, версию, синтаксис сценариев и состояние подписи необходимо проверять по отдельности.

Выполните изолированную установку и проверьте квитанцию

Окончательную приёмку следует выполнять в тестовой среде с возможностью отката, поскольку installer записывает данные в системные пути и создаёт установочные квитанции. Перед каждой задачей тестовый узел нужно возвращать в известное состояние; его не следует использовать совместно с повседневными сеансами разработки.

sudo installer -pkg "$PKG" -target /

test -x "/Library/Application Support/AcmeTool/bin/acmetool"
test "$(stat -f '%Sp' \
  '/Library/Application Support/AcmeTool/bin/acmetool')" = "-rwxr-xr-x"

pkgutil --pkg-info "com.example.acmetool.pkg"
pkgutil --files "com.example.acmetool.pkg" |
  sort > "$PKGDIR/installed-files.txt"

"/Library/Application Support/AcmeTool/bin/acmetool" --version

Успешная установка означает лишь то, что сценарий вернул нулевой код. Дополнительно нужно проверить, что исполняемый файл запускается, шаблон конфигурации корректно разбирается, а версия в квитанции верна. Журнал установки и список файлов следует сохранять как артефакты CI. При сбое сначала сохраните соответствующий временному интервалу фрагмент /var/log/install.log, а затем восстановите тестовую среду. Не запускайте задачу повторно сразу же, иначе исходные данные о сбое будут перезаписаны.

Итоговую проверку можно свести к пяти постоянным пунктам: в staging-каталоге нет лишних файлов; права и владельцы соответствуют контракту; версия пакета монотонно увеличивается; подпись официального артефакта успешно проверяется; после изолированной установки можно проверить и команду, и квитанцию. Такой подход превращает PKG из «архива, который можно открыть двойным щелчком» в проверяемый, воспроизводимый и пригодный для регрессионного тестирования инженерный артефакт.

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

Можно ли передать pkgbuild корень рабочего репозитория?

Не следует. Создайте отдельный staging-каталог только с файлами, которые должны попасть на целевую систему, иначе в PKG могут оказаться исходники, кэши, журналы и локальные атрибуты.

Можно ли проверить процесс без сертификата подписи Installer?

Да. Соберите неподписанный PKG, проверьте структуру, права, сценарии и тестовую установку, а перед распространением подпишите пакет доступной Installer-идентичностью и выполните pkgutil --check-signature.

Выделенный физический узел

Настройте облачный Mac для следующей сборки или задачи машинного обучения

Выберите одну из трёх конфигураций Apple Silicon, шести узлов и фиксированных сроков аренды. Фактическая доступность отображается в консоли в реальном времени.

Настроить облачный Mac