От подключения до первой сборки

Первый час
работы с облачным Mac

Первые полчаса посвятите подключению, проверке инструментов и пробной сборке, а вторые — настройке подписи и конвейера. Вы получаете выделенный физический Mac, а не виртуальную машину: привычные графический интерфейс macOS и командная строка остаются доступными.

Уже оформили заказ? Откройте консоль и проверьте фактический адрес хоста, порт, имя пользователя и данные для подключения. Не используйте примеры переменных как учётные данные.

00–10 минут

Проверьте отпечаток хоста и подключитесь по SSH.

10–20 минут

Проверьте Xcode, зависимости проекта и свободное место на диске.

20–30 минут

Сначала выполните проверку без подписи, затем проверьте archive.

Шаг 1 · Проверьте подключение

Используйте SSH для команд, а общий экран — для графического интерфейса

Сначала создайте ключ на локальном компьютере и передайте только открытый ключ. Закрытый ключ храните у себя и защитите паролем. Указанные в командах хост, порт и имя пользователя сверяйте с данными в консоли.

Выполните в локальном терминале macOS
read -r -p "Адрес хоста: " MAC_HOST
read -r -p "Порт SSH: " MAC_PORT
read -r -p "Имя пользователя: " MAC_USER
ssh-keygen -t ed25519 -f "$HOME/.ssh/osxhost_build"
cat "$HOME/.ssh/osxhost_build.pub"
ssh -p "$MAC_PORT" "$MAC_USER@$MAC_HOST"

Файл открытого ключа имеет расширение .pub . После первого подключения способом, указанным в данных для доступа, добавьте открытый ключ на удалённом хосте в файл ~/.ssh/authorized_keysи проверьте вход по ключу из другого локального терминала.

Добавьте открытый ключ на удалённый хост и смените пароль

На удалённом хосте создайте каталог ~/.sshи задайте ему права 700, а файлу открытого ключа — 600. Добавьте открытый ключ с помощью редактора, не удаляя уже выданные разрешения. Затем выполните passwd , чтобы сменить пароль текущего пользователя.

Откройте второй терминал и выполните ssh -i ~/.ssh/osxhost_build -p "$MAC_PORT" "$MAC_USER@$MAC_HOST" . Убедившись, что подключение работает, завершите исходный сеанс. Не закрывайте единственное доступное соединение раньше времени.

Проверка: вход по ключу в новом сеансе работает

Подключитесь к общему экрану через зашифрованный туннель

Выполните локально команду туннеля и не закрывайте терминал. В macOS откройте приложение «Общий экран» и подключитесь к vnc://127.0.0.1:5901. В Windows и Linux используйте клиент, поддерживающий соответствующий способ аутентификации VNC, и подключитесь к локальному порту 5901.

На удалённом хосте должен быть включён общий экран, а текущему пользователю должен быть предоставлен доступ. Учётные данные для графического входа и закрытый SSH-ключ — разные средства аутентификации. Не открывайте порт VNC для подключений из любых сетей.

Проверка: рабочий стол macOS отображается и доступен для управления
Локальный SSH-туннель · целевой адрес — loopback-интерфейс удалённого хоста
ssh -N -L 5901:127.0.0.1:5900 \
  -i "$HOME/.ssh/osxhost_build" \
  -p "$MAC_PORT" "$MAC_USER@$MAC_HOST"

Шаг 2 · Локальный Mac → облачный Mac

Перенесите данные по трём направлениям, не копируя старое окружение целиком

Сначала проверьте данные и инструменты, затем зарегистрируйте CI-исполнитель. Раздельно убедитесь, что вы можете войти в систему и что сборка запускается без участия пользователя: интерактивная настройка локального компьютера не гарантирует готовность конвейера.

Направление 01 · Данные

Перенесите проект, но не секреты в открытом виде

  • Экспортируйте из Связки ключей необходимые сертификаты подписи и закрытый ключ в зашифрованный файл .p12, а пароль передайте по отдельному защищённому каналу.
  • Переносите только профили подготовки, необходимые текущему проекту. Проверьте команду, идентификатор приложения и срок действия.
  • Не копируйте связку ключей для входа целиком. Создайте отдельную связку для сборки, импортируйте только нужные ключи и предоставьте доступ инструментам подписи.
  • Синхронизируйте исходный код и необходимые ресурсы, исключив каталоги зависимостей, DerivedData и старые артефакты сборки.

Проверка: версии проекта совпадают, сертификат содержит закрытый ключ, секретные файлы не попали в Git.

Направление 02 · Инструменты

Зафиксируйте версии, затем установите зависимости

  • Выполните xcodes list для просмотра доступных версий и установите нужную версию Xcode после получения необходимых разрешений.
  • С помощью xcode-select явно выберите текущий набор инструментов — не полагайтесь на последний выбор в графическом интерфейсе.
  • Проверьте Homebrew и инструменты arm64. Не переносите напрямую каталоги зависимостей из среды Intel.
  • Зафиксируйте версии Ruby, CocoaPods и fastlane с помощью менеджера версий Ruby и Gemfile.

Проверка: в терминале и CI используются одинаковые версии Xcode, Ruby и Bundler.

Направление 03 · Подключение CI

Разрешите исполнителю запускать только доверенные задачи

  • GitHub Actions: зарегистрируйте self-hosted runner для репозитория и выберите сборку для macOS ARM64.
  • GitLab Runner: зарегистрируйте исполнитель на уровне проекта, выберите shell-режим при необходимости и ограничьте круг проектов.
  • Jenkins: создайте агент macOS с изолированным рабочим каталогом и контролируемой передачей секретов.
  • Сначала настройте сборку с подписью и одним параллельным заданием, затем увеличивайте параллелизм с учётом пикового потребления памяти.

Проверка: после отправки доверенного коммита можно получить логи и артефакты сборки.

Синхронизация проекта с локального компьютера · сохранить файлы на удалённом хосте
rsync -avz \
  --exclude='.git/' \
  --exclude='build/' \
  --exclude='DerivedData/' \
  --exclude='Pods/' \
  --exclude='node_modules/' \
  -e "ssh -p $MAC_PORT -i $HOME/.ssh/osxhost_build" \
  ./ "$MAC_USER@$MAC_HOST:~/build-workspace/"

Эта команда не удаляет файлы на удалённом хосте. Для рабочего конвейера лучше получать фиксированный коммит из репозитория. В ключ кэша как минимум включите версии инструментов, файлы блокировки зависимостей и целевую архитектуру. Не позволяйте недоверенным внешним merge request получать доступ к секретам подписи.

Шаг 3 · Проверьте подпись и артефакты по отдельности

Свяжите match, gym и pilot в одном Fastfile

Выполните в корневом каталоге проекта bundle installи добавьте Gemfile.lock в репозиторий. Этот процесс предполагает, что репозиторий подписи уже настроен, приложение создано, а CI безопасно передаёт переменные окружения. Он не создаёт новые ресурсы для подписи.

Предварительная проверка на удалённом хосте · проверьте инструменты
xcode-select -p
xcodebuild -version
xcodebuild -showsdks
ruby --version
bundle --version
bundle exec fastlane --version
security find-identity -v -p codesigning
df -h "$HOME"

Укажите адрес репозитория подписи в переменной MATCH_GIT_URL, а пароль для расшифровки — в MATCH_PASSWORD. Для загрузки используйте серверный API-ключ: путь к файлу ключа задаётся переменной ASC_KEY_PATH , идентификатор ключа и идентификатор издателя — переменными ASC_KEY_IDиASC_ISSUER_ID . Файл ключа должен храниться в секретном хранилище CI и создаваться на диске во время выполнения. Не добавляйте его в репозиторий проекта.

Задайте переменные IOS_APP_IDENTIFIER,IOS_TEAM_ID,IOS_WORKSPACE и IOS_SCHEME. Тип распространения в репозитории подписи должен соответствовать appstore . Сначала проверьте shared scheme, затем запускайте полный конвейер.

fastlane/Fastfile · Конвейер TestFlight для одного приложения
default_platform(:ios)

platform :ios do
  desc "Archive a signed build and upload it to TestFlight"
  lane :release do
    setup_ci

    api_key = app_store_connect_api_key(
      key_id: ENV.fetch("ASC_KEY_ID"),
      issuer_id: ENV.fetch("ASC_ISSUER_ID"),
      key_filepath: ENV.fetch("ASC_KEY_PATH"),
      in_house: false
    )

    match(
      type: "appstore",
      readonly: true,
      git_url: ENV.fetch("MATCH_GIT_URL"),
      app_identifier: ENV.fetch("IOS_APP_IDENTIFIER"),
      team_id: ENV.fetch("IOS_TEAM_ID"),
      api_key: api_key
    )

    gym(
      workspace: ENV.fetch("IOS_WORKSPACE"),
      scheme: ENV.fetch("IOS_SCHEME"),
      configuration: "Release",
      clean: true,
      export_method: "app-store",
      output_directory: "build/artifacts",
      output_name: "release.ipa"
    )

    pilot(
      api_key: api_key,
      ipa: "build/artifacts/release.ipa",
      skip_waiting_for_build_processing: true
    )
  end
end
Запуск и проверка · переменные окружения передаёт CI
bundle exec fastlane ios release
find build/artifacts -maxdepth 1 -type f -print

Успешная загрузка ещё не означает, что обработка завершена.В этом примере ожидание обработки после загрузки пропущено. Проверьте статус сборки в консоли распространения и назначайте каждой загрузке уникальный номер сборки.readonly: true не позволяет CI изменять репозиторий подписи. Если срок действия сертификата или профиля подготовки истёк, обновите его в контролируемой среде и запустите процесс заново.

Кроссплатформенные проекты · сохраняйте файлы блокировки

Запускайте этап iOS отдельно, а кэш настройте с учётом его инвалидирования

Этапы iOS в Flutter и React Native зависят от Xcode, CocoaPods и среды подписи. Сначала выполните диагностику платформы, затем добавляйте загрузку сборки. Не пропускайте проверку совпадения зависимостей, даже если сработал кэш.

Flutter: сначала сборка для симулятора, затем экспорт IPA

Зафиксируйте версию Flutter SDK и сохраните файл pubspec.lock. Используйте общий кэш загрузок Pub. Для Pods ключом служит Podfile.lock, а для DerivedData — версия Xcode и настройки проекта. При пустом кэше первая загрузка и компиляция займут больше времени.

Выполните команду ниже в корневом каталоге проекта. Только после успешной настройки подписи командаflutter build ipa --release завершит сборку и экспорт приложения для реального устройства.

Не используйте кэш сборки повторно между основными версиями Xcode

React Native: зафиксируйте версии Node, Ruby и Pods

Добавьте в репозиторий файл блокировки менеджера пакетов JavaScript и используйте команду установки, принятую в проекте. Ниже приведён пример для npm. Зависимостями Ruby управляйте через Bundler, чтобы системные gem не конфликтовали с версиями проекта.

Используйте pod install --repo-updateтолько если новым зависимостям действительно нужно обновить индексы. В обычных случаях выполняйте bundle exec pod installи не удаляйте Pods и файлы блокировки при каждом запуске.

Добавляйте в ключ кэша архитектуру и файлы блокировки зависимостей
Корневой каталог проекта Flutter
flutter --version
flutter doctor -v
flutter pub get
flutter build ios --simulator
flutter build ipa --release
Корневой каталог проекта React Native · для проектов на npm
node --version
npm ci
bundle install
(cd ios && bundle exec pod install)
xcrun simctl list devices available

Ограничьте число параллельных тестов в симуляторах.Возьмите UDID двух доступных устройств из списка и задайте переменные SIM_UDID_AиSIM_UDID_B. Запустите тест в проекте с заданными значениями IOS_WORKSPACEиIOS_SCHEME . При первом запуске используйте одно устройство, затем добавьте второе и следите за нагрузкой на память и активностью диска.

Тестирование в Xcode · два целевых симулятора, поддерживаемых проектом
xcodebuild test \
  -workspace "$IOS_WORKSPACE" \
  -scheme "$IOS_SCHEME" \
  -destination "platform=iOS Simulator,id=$SIM_UDID_A" \
  -destination "platform=iOS Simulator,id=$SIM_UDID_B" \
  -parallel-testing-enabled YES \
  -maximum-concurrent-test-simulator-destinations 2 \
  -resultBundlePath "build/TestResults-$(date +%s).xcresult"

Контейнеры · Linux-сервисы отдельно, сборка macOS отдельно

Контейнеры подходят для вспомогательных сервисов, но не заменяют хост с Xcode

Сам узел — выделенный физический Mac. Контейнеры Linux в Docker Desktop или OrbStack по-прежнему работают в среде Linux. Не путайте «физический узел» с контейнером и macOS, которые якобы используют одно ядро.

Задача Рекомендуемая среда Что учесть
Тестовые базы данных, кэш и внутренние API Контейнер Linux По возможности используйте образы arm64 и ограничьте память и доступность портов.
Архивация в Xcode и тестирование в симуляторе Хост macOS Нужны SDK macOS, инструменты подписи и среда выполнения симулятора.
Сервисы, доступные только в образах amd64 Сначала проверьте совместимость Эмуляция архитектуры может увеличить накладные расходы: нельзя рассчитывать на производительность arm64.
Вспомогательные сервисы длительной работы Решение зависит от требований к изоляции ресурсов Сервисы используют общие с процессом сборки физическую память, SSD и сеть. Ограничивайте ресурсы и храните резервные копии.
После установки и запуска выбранного инструмента для контейнеров проверьте
uname -m
docker version
docker info --format '{{.Architecture}}'
docker system df
docker stats --no-stream

Выберите для управления контейнерами Docker Desktop или OrbStack и используйте только одну среду — так вы будете точно знать, куда указывает текущий Docker context. Перед добавлением сервисов проверьте объём свободной памяти и место на диске. Не удаляйте данные контейнеров как первый шаг диагностики.

Расширение конфигурации · сначала определите устройство, затем записывайте данные

Перенесите кэш на внешний диск, но не считайте несколько машин одной системой

Внешний SSD и объединение через Thunderbolt 5 — отдельные опции конфигурации. Название устройства, путь монтирования, поддержка интерфейсов и схема подключения зависят от данных заказа. Пока не проверите их, не запускайте команды форматирования и разметки диска.

Внешний SSD: проверьте точку монтирования и права

Выполните diskutil list external для обнаружения внешнего устройства, затем просмотрите сведения о нужном томе. Если том отформатирован, но не смонтирован, проверьте его идентификатор и выполните diskutil mount "$DISK_IDENTIFIER". Не делайте вывод, что диск — это внешний SSD, только по его номеру.

На диске можно хранить кэш зависимостей, DerivedData и временные артефакты. Укажите фактический путь в переменной CI BUILD_CACHE_ROOT, проверьте, что у пользователя сборки есть права на запись, и убедитесь, что путь доступен после перезагрузки. Резервные копии исходного кода и материалов для подписи храните отдельно.

Дополнительное место не заменяет резервное копирование

Объединение через TB5: проверяйте схему подключения каждого устройства

Объединение не превращает процессоры и унифицированную память нескольких машин в общий пул ресурсов одного процесса. Разделяйте задачи на уровне приложения или планировщика CI. На каждой машине отдельно настройте инструменты, исполнитель и рабочий каталог.

Если по схеме подключения используется сеть Thunderbolt, отдельно проверьте сетевой интерфейс, адреса и маршруты каждой машины, а затем протестируйте прямое соединение между узлами. До заказа укажите в обращении число машин, модели и задачи передачи данных. Не предполагайте, что у всех моделей одинаковые возможности интерфейсов.

Для распределённой обработки нужна поддержка со стороны приложения
Проверка удалённого хоста только на чтение · без изменений диска и сети
diskutil list external
ls -la /Volumes
df -h
system_profiler SPThunderboltDataType
networksetup -listallhardwareports

Цены на четыре варианта срока аренды расширения указаны на странице конфигураций. Если нужно изменить уже оформленный заказ, сначала отправьте обращение и согласуйте порядок работ и переноса данных. Не меняйте структуру диска во время сборки в рабочей среде.

Последний шаг · Найдите причину по диагностическим данным

Сначала соберите минимум диагностических данных, затем меняйте настройки

Запишите номер заказа, узел, время сбоя с часовым поясом и номер коммита последней успешной сборки. Сохраните первое сообщение об ошибке и код завершения, а не только последнюю строку лога. Команды ниже помогут сузить круг причин, но не исправляют проблемы автоматически.

01

Не удаётся подключиться по SSH

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

Локальный терминал · вывод может содержать сведения о хосте
ssh -vv -o ConnectTimeout=10 \
  -i "$HOME/.ssh/osxhost_build" \
  -p "$MAC_PORT" "$MAC_USER@$MAC_HOST"
Создать обращение по проблеме с подключением
02

Сертификат недействителен или не найдена личность для подписи

Проверьте, содержит ли сертификат закрытый ключ, не истёк ли срок его действия и соответствуют ли профиль подготовки, приложение и команда друг другу. Связка ключей, используемая CI, также должна быть доступна в сеансе сборки. Сначала исправьте ресурсы для подписи в контролируемой среде, затем синхронизируйте их через match в режиме только для чтения.

Удалённый терминал · список сертификатов для подписи и связок ключей
security find-identity -v -p codesigning
security list-keychains -d user
bundle exec fastlane match appstore --readonly
Создать обращение по проблеме с подписью
03

На диске нет места, архив не записывается

Сначала выясните, какой том заполнен — системный или внешний, — и проверьте, сколько места занимают DerivedData, симуляторы и артефакты сборки. Удаляйте только подтверждённые как восстанавливаемые кэши. Не удаляйте массово связки ключей, файлы подписи или используемые рабочие каталоги.

Удалённый терминал · только просмотр занятого места
df -h
du -sh "$HOME/Library/Developer/Xcode/DerivedData" \
  "$HOME/Library/Developer/CoreSimulator" \
  "$HOME/build-workspace" 2>/dev/null
xcrun simctl list devices unavailable
Создать обращение по проблеме с диском
04

Xcode долго выполняет индексацию

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

Удалённый терминал · проверка процессов и конфигурации сборки
ps -axo pid,%cpu,%mem,etime,comm | grep -E 'Xcode|SourceKit|swift'
xcode-select -p
xcodebuild -list -workspace "$IOS_WORKSPACE"
df -h "$HOME"
Создать обращение по проблеме с Xcode

Не прикладывайте к обращению закрытые ключи, пароли сертификатов, токены доступа или содержимое репозитория подписи целиком.Перед отправкой скройте секреты и чувствительные пути в логах. Если не удаётся войти в консоль, напишите на адрес support@osxhost.com, указав номер заказа и описание проблемы. Электронная почта и обращения через консоль — официальные каналы поддержки.

Сначала добейтесь успешной архивации, затем увеличивайте параллелизм

Укажите в конвейере версии инструментов, передачу данных для подписи, ключи кэша и каталог артефактов. Чтобы заказать машину, оформите заказ; если возникла проблема, отправьте обращение с диагностическими данными.