Разработка
Куда девается место на диске: Xcode, рабочие деревья и ИИ-агенты
Такое начинает происходить гораздо чаще, когда в рабочем процессе появляются ИИ-агенты.
Несколько недель назад мои сборки начали странным образом падать. Сначала сломался этап линковки. Потом lipo. Затем кэш модулей начал жаловаться, что файл «был изменён после того, как был собран module file». Чистые сборки не помогали.
Оказалось, диск был заполнен. И ни одна из этих ошибок прямо об этом не говорила.
Такое начинает происходить гораздо чаще, когда в рабочем процессе появляются ИИ-агенты. Я запускаю по два-три агента одновременно, каждый в своём git worktree, каждый собирает приложение и запускает тесты. Каждое рабочее дерево также получает собственные копии симуляторов. Если агенты используют один и тот же симулятор, они начинают устанавливать свои сборки поверх чужих и нажимать кнопки на чужих экранах. Каждая сборка, каждый прогон тестов и каждая копия симулятора оставляют после себя гигабайты данных, и ничего из этого автоматически не очищается.
Кратко
- Если сборка падает странным образом, сначала выполните
df -h /System/Volumes/Data. - Доверяйте
df, а неdu: из-за клоновduзавышает размер. - Очищайте DerivedData, результаты тестов и симуляторы worktree при его удалении — с помощью навыка, хука или обоих.
- Направляйте каждую сборку одного и того же worktree в один и тот же DerivedData.
- Когда места мало, удаляйте целые восстанавливаемые папки, а не их отдельные части.
- Время от времени запускайте
mo analyzeи смотрите, куда уходит место.
Xcode не скажет вам, что диск заполнен
Когда диск заканчивается прямо во время сборки, вы редко увидите ошибку «No space left on device». У меня встречались такие варианты:
- Ошибки линковщика или
lipo. Там действительно может упоминаться «No space left on device», но где-то глубоко в длинном логе. - Ошибки кэша модулей вроде «has been modified since the module file», оставшиеся после недописанного кэша.
- Стена ошибок системы сборки. Однажды сборка вывела 23 строки с «unexpected incomplete target», а в самом конце оказалась настоящая причина:
database or disk is full.
Поэтому первое правило: если сборка падает так, что причина совершенно непонятна, сначала проверьте диск. И читайте последнюю ошибку, а не первую.
Измеряйте правильно
У проверки свободного места тоже есть свои ловушки.
df -h / врёт. В современных версиях macOS / — это запечатанный системный том только для чтения. Однажды у меня он показывал 5,2 ГБ свободного места, хотя том, где реально находились мои файлы, был заполнен на 99%. Проверяйте именно том данных:
df -h /System/Volumes/Data
du считает одни и те же данные несколько раз. Xcode и симулятор копируют файлы через APFS clones, которые используют общее место до тех пор, пока одна из копий не изменится. du считает каждую копию как будто она занимает полный размер. Однажды я удалил папку, которую du показывал как 3 ГБ, а освободилось около 90 МБ, потому что DerivedData всё ещё ссылался на те же блоки. Собранный worktree приложения, над которым я работаю, выглядел в du примерно вдвое больше своего реального размера.
Поэтому оценивайте результат очистки по тому, насколько изменился df, а не по тому, сколько якобы весила папка согласно du.
Куда уходит место
Сколько именно места занимает каждый из этих элементов, зависит от вашего приложения: его размера, числа пакетов и количества тестов. Но от приложения не зависит что именно растёт. Всё это находится внутри ~/Library/Developer/:
| Что | Где | Растёт из-за |
|---|---|---|
| Прогоны тестов агентами | XcodeBuildMCP/workspaces/ |
каждого запуска тестов |
| Удалённые worktree | Xcode/DerivedData/ |
каждого удалённого worktree |
| Клонированные симуляторы | CoreSimulator/Devices/ |
каждого клона и установленных в нём приложений |
| Кэши модулей | Xcode/DerivedData/ModuleCache.noindex |
обновлений Xcode и SDK |
Для понимания масштаба: я работаю над модульным UIKit-приложением примерно из 300 000 строк Swift в 70 пакетах. У меня один прогон тестов оставлял 6–7 ГБ, собранный worktree занимал 25–30 ГБ, а клонированный симулятор — около 3 ГБ. У вас эти цифры могут быть в десять раз меньше. Или больше.
Больше всего меня удивила первая строка. XcodeBuildMCP, который мои агенты используют для сборки и тестирования, сохраняет полную копию тестовых продуктов для каждого запуска тестов вне DerivedData, поэтому обычная очистка Xcode их не затрагивает. За один день UI- и snapshot-тестов там накопилось 96 ГБ, и диск дважды полностью заполнялся.
Как очистить Xcode от мусора и освободить ценное дисковое пространство на вашем Mac
Вторая строка — это медленная утечка. Каждый удалённый worktree оставляет после себя папку DerivedData, а если вы дополнительно собираете его через обычный xcodebuild, появляется ещё одна папка. Я нашёл 65 осиротевших каталогов. Отличить их можно по info.plist: у активной папки WorkspacePath указывает на существующий проект. У осиротевшей info.plist либо отсутствует, либо путь ведёт туда, чего уже нет.
Чтобы посмотреть свои цифры, выведите самые большие папки в каждом месте:
cd ~/Library/Developer du -sh XcodeBuildMCP/workspaces/* Xcode/DerivedData/* CoreSimulator/Devices/* \ 2>/dev/null | sort -h | tail -15
du вполне подходит для поиска самых больших каталогов. Просто не рассчитывайте, что их удаление освободит ровно столько места, сколько он показывает — причины я объяснил выше.
Очищайте всё вместе с worktree
Лучший момент для очистки — когда вы удаляете worktree. В этот момент вы точно знаете, что принадлежало именно ему, поэтому позже ничего не придётся угадывать:
- Сам worktree и его ветка.
- Его папки DerivedData: все каталоги, у которых в
info.plistзначениеWorkspacePathуказывает внутрь этого worktree. - Его workspace XcodeBuildMCP, названный по имени каталога worktree.
- Его симуляторы. Называйте клоны по имени worktree — я использую
wt-<ticket>-<device>— и их будет легко найти. - Всё остальное, что вы храните отдельно для каждого worktree, например скриншоты или различия тестов.
Я оформил это как навык для агента: файл инструкций cleanup-worktree, которому агент следует после слияния PR. Он проверяет, что PR действительно слит и не осталось незакоммиченных изменений, удаляет всё перечисленное выше и сообщает, сколько места освободилось. Он также ищет осиротевшие данные от worktree, удалённых раньше, но перед их удалением спрашивает подтверждение, потому что сопоставление здесь уже строится на догадках.
Если не хочется полагаться на то, что вы вспомните запустить очистку, автоматизируйте её. Я управляю worktree с помощью worktrunk, который умеет запускать хуки при удалении worktree. Пользовательский хук может каждый раз очищать остатки Xcode:
# ~/.config/worktrunk/config.toml
[post-remove]
xcode-leftovers = """
cd ~/Library/Developer
# XcodeBuildMCP names workspaces <worktree>-<12-character hash>
for dir in XcodeBuildMCP/workspaces/*; do
case "${dir##*/}" in
{{ worktree_name }}-????????????) rm -rf "$dir" ;;
esac
done
for plist in Xcode/DerivedData/*/info.plist; do
case "$(plutil -extract WorkspacePath raw "$plist" 2>/dev/null)" in
{{ worktree_path }}/*) rm -rf "${plist%/info.plist}" ;;
esac
done
Навык и хук хорошо дополняют друг друга: хук гарантирует, что очевидные вещи удалятся всегда, а навык может принимать решения там, где нужен контекст, например действительно ли ветка уже слита.
Перестаньте создавать новые копии
Очистка — только половина решения. Вторая половина — не создавать дубликаты изначально.
Используйте один DerivedData на каждый worktree. Мои агенты собирают через XcodeBuildMCP, который использует собственный путь к DerivedData. Но некоторые скрипты вызывали xcodebuild напрямую с путём по умолчанию, и один и тот же worktree в итоге собирался дважды в двух разных местах. Теперь каждый прямой вызов xcodebuild получает тот же -derivedDataPath, что и MCP. Вторая сборка уже ничего не компилирует.
Оставьте свои сборки Xcode отдельно. Я специально оставил сборки из самого приложения Xcode в стандартном DerivedData. Так открытие worktree в Xcode никогда не конфликтует со сборкой агента за одну и ту же папку.
Удаляйте целые деревья, а не отдельные части. Из-за клонов удаление только тестовых продуктов почти ничего не освобождает. Когда диск действительно забит, реально помогало удаление:
- кэшей модулей (
ModuleCache.noindexиSDKExplicitPrecompiledModules), которые сразу создаются заново при следующей сборке; - всего workspace XcodeBuildMCP для worktree, которого больше не существует;
- клонов симуляторов, которыми никто не пользуется.
Сначала посмотрите, потом очищайте
Прежде чем что-то делать, полезно своими глазами увидеть, куда именно ушло место. Mole (brew install mole) — бесплатная утилита с открытым исходным кодом для очистки Mac с очень хорошим обозревателем диска:
mo analyze
Она показывает диск в виде дерева, по которому можно перемещаться от самых больших папок вниз. Это самый быстрый способ понять, куда ушло место, прежде чем что-либо удалять. У неё также есть команды mo clean и mo purge для кэшей и артефактов сборки, причём обе поддерживают --dry-run, чтобы заранее увидеть, что именно будет удалено.
Но я бы запускал её вручную, а не отдавал агенту. Смысл в том, чтобы вы сами понимали, куда уходит место, а потом автоматизировали только те части, в которых уже разобрались.
Ничего из этого несложно, но само по себе ничего не происходит. Один раз запустите mo analyze, чтобы понять, куда уходит дисковое пространство, а затем поручите хуку или навыку очищать всё после каждого worktree — и об этой проблеме можно будет почти перестать думать.
-
Разработка2 недели назадДаем ИИ видеть SwiftUI: Xcode Preview MCP на практике — проблемы и надежды
-
Новости3 недели назадВидео и подкасты о мобильной разработке 2026.39
-
Новости4 недели назадВидео и подкасты о мобильной разработке 2026.38
-
Новости2 недели назадВидео и подкасты о мобильной разработке 2026.40
