Производительность 15 апреля 2026

2026-04-15 Xcode DerivedData, TMPDIR и xcresult на арендованном облачном Mac CI

Инженерная команда MacXCode 15 апреля 2026 ~13 мин

Команды, арендующие Mac mini M4 в Гонконге, Токио, Сеуле, Сингапуре и востоке США, часто винят «медленный Xcode», хотя корень — конкуренция за ФС: два пайплайна пишут один и тот же кусок DerivedData, UI-тест заполняет /tmp, а .xcresult раздувается свыше 6 ГБ и голодает параллельные архивы. Материал с меткой 2026-04-15 объясняет, кому нужна изоляция на задачу, перечисляет сбои на headless SSH, сравнивает стратегии таблицей, даёт восьмишаговый shell-runbook и ссылается на SwiftPM: реестр и кэш и авто vs ручная подпись.

Apple Silicon на голом железе снижает CPU-шум, но пропускная способность NVMe ограничена. Общий ~/Library/Developer/Xcode/DerivedData порождает стохастические ошибки SwiftEmitModule. Свяжите DERIVED_DATA_PATH, TMPDIR, -resultBundlePath и -clonedSourcePackagesDirPath с одним префиксом задачи — тогда ёмкость станет измеримой, а сравнение региональных узлов честным.

Кому нужен отдельный DerivedData на облачном Mac

Изоляция окупается, если в скользящем окне 24 часа могут одновременно работать три и более процессов xcodebuild с одной семьёй workspace — типично для trunk-based iOS или white-label. Без отдельных путей XCTest-вложения пропадают, карты модулей устаревают. Предсказуемое дерево на задачу превращает вопрос «арендовать второй M4?» в анализ дисковых кривых.

Сбои на headless SSH-сборщиках

  • Индексация во время сборки — фон задачи A меняет индекс, который задача B читает посередине («тип не найден» до clean).
  • Общий TMPDIR — SwiftPM/clang оставляют сотни тысяч мелких файлов; чужой cleanup удаляет ещё открытый префикс.
  • xcresult без квот — видео и скриншоты UI: три набора > 8 ГБ.
  • Чередование архива и тестов — ночной архив и сразу интеграционные тесты на том же DerivedData повышают риск при обрыве архива.
Золотое правило: считайте DERIVED_DATA_PATH, TMPDIR, путь результата и -clonedSourcePackagesDirPath единым префиксом задачи, создаваемым при постановке в очередь и удаляемым через trap.

Сравнение стратегий изоляции

Подход Плюсы Минусы Когда
Per-job DERIVED_DATA_PATH под /Volumes/builds Детерминированные очистки, квоты на пайплайн Первый билд без тёплого кэша медленнее Параллельный CI на общем Mac mini M4
Тёплый read-only + overlay-копия Быстрее холодные компиляции Сложный rsync, трудно на чистом SSH Релизные поезда с одинаковыми минорами Xcode
Разные учётные записи macOS Жёсткая изоляция подписей Выше операционные затраты и лицензии Регулируемые мультитенанты

Бюджет NVMe: цифры для runbook

Артефакт Типичное состояние Пик UI-тестов
DerivedData (среднее приложение, Debug) 6–14 ГБ + 3 ГБ scratch
SwiftPM checkout + сборка 1–4 ГБ + 2 ГБ при новых тегах
Бандл xcresult 400–900 МБ unit 3–10 ГБ с видео
Запас: держите ≥ 35 % свободно на томе задачи; если свободно < 50 ГБ, приостановите новые задачи и удаляйте только префиксы старше 36 часов, не занятые PID.

Восьмишаговый runbook для арендованных облачных Mac

  1. МеткаJOB_ID=$(date +%s)-$RANDOM, ROOT=/Volumes/builds/$JOB_ID, mkdir -p.
  2. Окружение — экспорт DERIVED_DATA_PATH, TMPDIR, TEMP, TMP.
  3. trap — после проверки уникальности JOB_ID зарегистрируйте trap 'rm -rf "$ROOT"' EXIT.
  4. xcodebuild — тесты: -resultBundlePath "$ROOT/results/Test-$JOB_ID.xcresult"; IPA в объектное хранилище по гайду IPA.
  5. SwiftPM-clonedSourcePackagesDirPath "$ROOT/spm" + авторизация реестра.
  6. Параллелизм — на 24 ГБ RAM тяжёлых UI-тестов ≤ 2; мьютекс в очереди.
  7. Загрузкаditto -c -k --sequesterRsrc --keepParent, контрольная сумма, удаление zip.
  8. Телеметрияdf -h до/после; тревога, если стена > 42 мин и диск > 90 %.

export JOB_ROOT=/Volumes/builds/ci-$CI_PIPELINE_ID mkdir -p "$JOB_ROOT"/{dd,tmp,res,spm} export DERIVED_DATA_PATH="$JOB_ROOT/dd" TMPDIR="$JOB_ROOT/tmp" TEMP="$JOB_ROOT/tmp" TMP="$JOB_ROOT/tmp"

Метрики за полдня

Без наблюдения изоляция бессмысленна. Три датчика: (1) du -sk "$ROOT" после компиляции, (2) секунды xcodebuild archive, (3) свободные ГБ на томе /Volumes/builds. Если медиана DerivedData растёт > 18 % неделя к неделе, ищите отключённый incremental или тяжёлую codegen, а не сразу железо. Логируйте xcodebuild -version рядом с JOB_ID, чтобы коррелировать смену 16.2/16.3. xcrun simctl delete unavailable — вне пиков.

Мост к SwiftPM и дисциплине подписи

Изоляция DerivedData не заменяет provisioning. Еженедельная ротация сертификатов требует авто vs ручная подпись, чтобы codesign не ждал UI. Держите Package.resolved под VCS и не смешивайте глобальный SourcePackages с per-job DerivedData.

FAQ: DerivedData на арендованном Apple Silicon

Вопрос Практический ответ
Симлинк DerivedData на NFS? Избегать, если RTT не < 2 мс; лучше локальный NVMe + выгрузка артефактов.
Легаси Xcode Server? Современный CI игнорирует /Library/Developer/XcodeServer; всегда явные пути.
Rosetta? Не смешивайте x86_64-симуляторы с arm64-префиксами; изолируйте по архитектуре.

Почему региональные узлы Mac mini M4 всё ещё важны

Каталоги убирают гонки ПО, но не физику: тянуть LFS из US East в Токио стоит минут RTT. Размещайте /Volumes/builds на том же континенте, что SCM и аудит. Читайте справку по SSH и масштабируйте параллелизм после 14 зелёных ночей по диску.

Итог: per-job DERIVED_DATA_PATH + TMPDIR + явные пути xcresult превращают случайные красные сборки в измеримые IO-бюджеты — затем решайте по ценам, а не интуиции.

Масштабируйте изолированные билдеры на M4

HK · JP · KR · SG · US · SSH / VNC