Самый быстрый способ убить пропускную способность Apple Silicon CI — назвать кэши по веткам Git. Ключи веток переиспользуют графы между коммитами, где тихо меняются Package.resolved, Podfile.lock или Gemfile.lock, давая «случайные» красные и хуже — фантомно-зелёные. В 2026 году относитесь к кэшам зависимостей как к content-addressed storage: хэшируйте lockfiles, фиксируйте номер сборки toolchain и публикуйте политику, чтобы каждый инженер понимал: промах кэша — это нормально.
Операционный контекст: диск и удержание артефактов, worktree против чистого клона и аффинити раннеров. Если нужны более изолированные хосты Mac mini M4 в Гонконге, Японии, Корее, Сингапуре или США, сравните цены и следуйте справке по SSH/VNC.
Почему хэши lockfile сильнее меток веток
Метки веток подразумевают, что «всё на этой линии разработки делит состояние». Графы зависимостей не следуют линиям — они следуют точным результатам резолвера. Один merge может поднять транзитивную версию без изменения прикладного кода, инвалидируя бинарники при неизменном имени ветки. Хэширование lockfiles делает попадание в кэш обязательством доказать: при совпадении хэша есть доказательство, что вывод резолвера идентичен с точностью до toolchain.
- Воспроизводимость: аудиторы связывают зелёную сборку с конкретным дайджестом lockfile.
- Параллельные PR: два pull request с одним именем ветки (fork) больше не сталкиваются незаметно.
- Экономия диска: LRU по хэшам лучше ограничивает рост, чем «удалить всю папку ветки».
Матрица A: рецепт ключей по экосистеме
| Экосистема | Входы блокировки | Рекомендуемый суффикс пути |
|---|---|---|
| SwiftPM + Xcode | Package.resolved + сборка Xcode |
spm/<sha256(resolved)>/xc<build> |
| CocoaPods | Podfile.lock + файл версии Ruby |
pods/<sha256(lock)>/ruby<ver> |
| Bundler | Gemfile.lock |
bundle/<sha256(lock)> |
| npm / pnpm | package-lock.json или pnpm-lock.yaml |
js/<sha256(lock)>/node<major> |
Матрица B: когда общие кэши всё ещё опасны
Даже идеальное хэширование lockfile не спасёт, если post-install скрипты выполняются при восстановлении. Считайте такие джобы холодным путём, если не песочница для скриптов и проверка сумм восстановленных артефактов.
| Сигнал | Политика | Смягчение |
|---|---|---|
Хуки post_install меняют Pods |
Не делить restore между джобами | Запускать хуки каждую сборку; кэшировать только скачанные blob |
| Бинарные Pods без проверки контрольной суммы | Считать недоверенным вводом | Vendor с контент-хэшами в репозитории |
| Сборки fork PR | Отдельное пространство имён кэша | Никогда не переиспользовать пути кэша внутреннего монорепо |
Числовые дефолты для аудита
- Алгоритм хэша: SHA-256 нормализованных байтов lockfile (в CI унифицируйте CR/LF).
- Ширина LRU: храните последние 12 хэшей на экосистему на хост.
- Максимальный возраст: вытесняйте неиспользуемые хэши после 14 дней вне зависимости от LRU.
- Алерты: пейджинг, когда том кэша превышает 70% выделенного CI-диска.
Безопасность: кэши — часть цепочки поставок. Если скомпрометированный lockfile может отравить общий tarball-кэш, предпочтите эфемерную загрузку с проверкой вместо слепого переиспользования.
Восемь шагов внедрения
- Инвентаризация: какие репозитории без закоммиченных lockfiles — блокируйте кэш, пока их нет.
- Эмитировать дайджест как CI-артефакт для каждой сборки для последующей корреляции.
- Монтировать кэши на быстрые тома APFS, не сетевые шары, для разрешения SPM.
- Метрики: доля попаданий, секунды restore, счётчики вытеснения.
- Документировать ожидания холодного кэша в CONTRIBUTING.md.
- Явно тестировать fork-воркфлоу — разделять кэши по уровню доверия.
- Сочетать с политикой checkout из гайда по worktree, чтобы избежать двойного шаринга.
- Масштабировать железо при thrash вытеснения — NodeMac добавляет выделенные узлы M4 по регионам без закупочных задержек.
FAQ
Почему кэши по имени ветки дают фантомный зелёный?
Они переиспользуют бинарники между сменами lockfile. Хэшируйте lockfiles, чтобы попадание означало идентичный вывод резолвера.
Общий ключ SPM и DerivedData?
Нет — разделение ответственности. SPM относится к разрешению, DerivedData — к индексам компиляции и должен включать измерения джоба или toolchain.
Как часто подрезать?
LRU в пределах капа плюс вытеснение по максимальному возрасту; всегда связывайте с дисковыми алертами.