2026-04-15 Xcode DerivedData, TMPDIR и xcresult на арендованном облачном Mac CI
Команды, арендующие 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 ГБ с видео |
Восьмишаговый runbook для арендованных облачных Mac
- Метка —
JOB_ID=$(date +%s)-$RANDOM,ROOT=/Volumes/builds/$JOB_ID,mkdir -p. - Окружение — экспорт
DERIVED_DATA_PATH,TMPDIR,TEMP,TMP. - trap — после проверки уникальности
JOB_IDзарегистрируйтеtrap 'rm -rf "$ROOT"' EXIT. - xcodebuild — тесты:
-resultBundlePath "$ROOT/results/Test-$JOB_ID.xcresult"; IPA в объектное хранилище по гайду IPA. - SwiftPM —
-clonedSourcePackagesDirPath "$ROOT/spm"+ авторизация реестра. - Параллелизм — на 24 ГБ RAM тяжёлых UI-тестов ≤ 2; мьютекс в очереди.
- Загрузка —
ditto -c -k --sequesterRsrc --keepParent, контрольная сумма, удаление zip. - Телеметрия —
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