Поисковый интент: выбрать между прямым fetch, git bundle и shallow на удалённом Mac CI, когда RTT велик и канал рвётся. Здесь — три боли, две таблицы, шесть шагов с параметрами, опорные цифры и ссылки на смежную матрицу провайдеров, кеш Gradle, каталог блога и главную MacPull.

Три узких места до стабильного checkout:

  1. Раздувание графа объектов. Полный git clone тянет историю, которую сборке не нужно; при нестабильном WAN обрыв на миллионах объектов стоит дороже, чем повтор короткого среза.
  2. Смешение таймаутов HTTP и логики job. Без GIT_HTTP_LOW_SPEED_LIMIT раннер держит слот, пока сокет «ползёт»; внешне это похоже на медленный компилятор, хотя проблема — только транспорт Git.
  3. Гонки на общем object store. Неверный alternates или запись в зеркальный каталог из параллельных джобов даёт повреждённые pack-файлы; без git bundle verify и fsck дефект всплывает на этапе линковки.

Трансграничный сценарий: задержка, джиттер и политика сокетов Git

На удалённом Mac в облаке первичный риск — не CPU, а длинный маршрут до корпоративного forge. Зафиксируйте переменные до любого clone: export GIT_HTTP_LOW_SPEED_LIMIT=5000 и export GIT_HTTP_LOW_SPEED_TIME=600 — так Git прервёт зависший HTTP, не дожидаясь дефолтных десятков минут. Для SSH используйте GIT_SSH_COMMAND="ssh -o ConnectTimeout=15 -o ServerAliveInterval=30" чтобы отличать обрыв туннеля от ошибки авторизации.

Предел полезной глубины: если job читает только HEAD ветки релиза, --depth=32 обычно достаточно для blame и короткой истории; увеличивайте глубину только когда аналитика требует полного лога.

Сравнение транспортов: где bundle, где shallow

Режим Объём по сети Контроль целостности
Полный clone по HTTPS Максимальный; растёт с историей Стандартный index-pack; медленный повтор при обрыве
git clone --filter=blob:none --depth=N Низкий за счёт отложенных blob Последующие git fetch должны уважать ту же глубину
git bundle между двумя SHA Детерминированный файл артефакта Обязателен git bundle verify перед распаковкой
Зеркало + alternates только чтение Почти нулевой при тёплом кеше Требуется изоляция записи и периодический git fsck --connectivity-only

Стратегия срезов git bundle между базовой и вершиной ветки

Соберите артефакт на стабильном билдере внутри периметра: git bundle create ci-OLD..NEW.bundle OLD_SHA..NEW_SHA --progress. Имя файла должно содержать оба конца диапазона, чтобы ключ кеша CI однозначно ссылался на набор объектов. На Mac-раннере: git clone --depth=1 file:///path/ci-OLD..NEW.bundle workspace, затем git remote set-url origin https://forge.example.com/org/repo.git и git fetch origin refs/heads/main:refs/remotes/origin/main с той же глубиной.

git bundle verify ci-OLD..NEW.bundle
git clone --depth=32 --filter=blob:none --single-branch \
  file:///Volumes/cache/ci-OLD..NEW.bundle work
cd work && git remote set-url origin "$FORGE_URL"

Повторное использование объектного хранилища: alternates и запрет записи

Разместите git clone --mirror на отдельном томе и укажите абсолютный путь в .git/objects/info/alternates либо экспортируйте GIT_ALTERNATE_OBJECT_DIRECTORIES=/Volumes/git-mirror/repo.git/objects. Каждая джоба пишет только в свой .git/objects; очиститель pack-файлов не должен трогать зеркало. Для одноразовых воркеров допустим GIT_OBJECT_DIRECTORY указывающий на tmpfs, но тогда bundle остаётся единственным источником новых объектов.

Повторные fetch при сбоях и составление ключа кеша CI

Оберните git fetch в цикл с паузами две четыре восемь секунд и потолком пяти попыток на стадию. Логируйте GIT_TRACE_CURL=1 только при отладке, иначе артефакты раздуваются. Ключ кеша соберите как конкатенацию: идентификатор пула раннеров, имя ветки, короткий SHA вершины, целое depth, строка фильтра (blob:none или пусто), SHA-256 имени bundle-файла. Любое изменение глубины или фильтра обязано промахивать кеш, иначе вы получите частичный граф без нужных коммитов.

for i in 1 2 3 4 5; do
  git fetch --depth=32 --prune origin "+refs/heads/$BRANCH:refs/remotes/origin/$BRANCH" && break
  sleep $((2**i))
done

Матрица решений: когда комбинировать bundle и shallow

Сигнал Рекомендуемая связка Риск если игнорировать
RTT > 180 мс и частые обрывы bundle ночью + shallow clone из файла Долгие висящие слоты MacPull
Нужен blame глубже N коммитов Увеличить depth, отказаться от минимального shallow «missing blob» при checkout старых файлов
Несколько репозиториев одного монорепо-зеркала Общий alternates + локальные packs Коррупция при записи в зеркало
За forge включён WAF с лимитом тела Меньшие bundle-файлы по двум промежуточным SHA HTTP 413 и молчаливый retry

Операционный конвейер: шесть шагов с приёмкой

Шаг 1. Экспорт лимитов WAN и SSH keepalive в профиль службы раннера.

Шаг 2. Скачать или смонтировать bundle, выполнить git bundle verify; при ошибке — не распаковывать.

Шаг 3. git clone с --filter=blob:none и согласованной глубиной; зафиксировать git rev-parse HEAD в артефакте.

Шаг 4. Подключить alternates или чистый удалённый origin; проверить git remote -v.

Шаг 5. Выполнить git fsck --connectivity-only после первого успешного fetch.

Шаг 6. Сохранить строку ключа кеша и журнал попыток fetch для постмортема.

Три опорных факта для отчёта: доля успешных bundle verify с первого раза; p95 длительности git fetch при depth 32; число промахов кеша после смены только фильтра без смены SHA bundle.

Следующий шаг: узел, тарифы и справка без входа

Закрепите Apple Silicon рядом с вашим egress и повторите матрицу на реальном канале. Откройте список статей блога, сравните тарифы, оформите аренду или начните с центра помощи по SSH и доступу; обзор платформы — на главной MacPull.

Mac Mini M4 под стабильный Git checkout в CI

Выберите узел рядом с вашим forge, зафиксируйте shallow и bundle в одном пайплайне без обязательного входа на маркетинговые страницы.