package-lock.json. На удалённом Mac CI за трансграничным каналом чаще ломает двойная резолюция, разные URL registry и «глупый» retry. Ниже — матрица и копируемые env. Без входа: главная MacPull, блог; см. Yarn Berry и Deno/JSR — без углубления в Git, Docker или SwiftPM.
Три типичных отказа гибридного Bun + npm на удалённом Mac
Узкое место — RTT и лимиты registry, не CPU Apple Silicon.
- Двойной lockfile:
npm installв корне при CI наbun install --frozen-lockfile(или наоборот) даёт «случайный» дрейф. - Разные registry:
NPM_CONFIG_REGISTRY/bunfig.toml/.npmrcрасходятся — checksum mismatch на общих раннерах. - Нет капа сокетов: волна
429; внешний retry job маскирует integrity.
Исполняемая матрица решений (гибрид + трансграничный registry)
Строки — ворота merge; на мультитенантных Mac важнее лимит job’ов на зеркало, чем dedup tarball’ов.
| Решение | Вариант A | Вариант B | Примечание для удалённого Mac CI (2026) |
|---|---|---|---|
| Владелец lockfile | Bun.lockb / bun.lock; корень: bun install --frozen-lockfile, без npm install |
Только package-lock.json; Bun не пишет lock |
Не перегенерировать оба lock в CI; по Bun.lockb — checksum + лог. |
| Зеркало registry | NPM_CONFIG_REGISTRY + scoped токен в секретах |
Региональное зеркало + bunfig.toml при политике |
Один hostname на job для npm ci и bun install. |
| Параллельная установка | Bun по умолчанию; кап одновременных job на Mac (1–2) | npm config set maxsockets 12 (8–16, метрики 429) |
Свыше ~24 сокетов на высоком RTT часто растёт 429, не p50. |
| Ключ кеша CI | bun-<os>-<hash(lock)>-<hash(package.json**)> |
npm-<os>-<hash(package-lock)>-<node>-<npm> |
Добавляйте мажор Node и семвер npm/Bun при влиянии на optional. |
| Повторы | Сеть: ≤3, backoff 2/4/8 с | Lockfile / integrity: 0 внешних retry | Не оборачивать ERESOLVE, frozen-lockfile, checksum. |
Пять шагов: назначить владельца lock → один registry URL → NPM_CONFIG_MAXSOCKETS плюс лимит job на узел → ключи кеша по lock и версиям → grep-ворота на checksum/frozen-lockfile без внешнего retry.
Для ссылок в runbook: коридор 8–24 параллельных загрузок до метрик; сеть — до трёх попыток с 2/4/8 с; ключ кеша — хеш lock плюс семвер Bun или npm.
Bun.lockb и package-lock.json в одном репозитории
Bun.lockb vs package-lock.json — разные резолверы. Легаси на npm: отдельный корень + npm ci; корень monorepo на Bun — правило в README. В метаданных job печатайте версии Bun/npm; на arm64 сверяйте optional native.
Если команда случайно запускает обновление lock «чужим» менеджером, восстановление начинается с отката коммита и повторной генерации одним выбранным инструментом на чистом клоне. Не смешивайте артефакты кеша Bun и npm в одном каталоге без префикса job — иначе тёплый раннер подставит чужие tarball’ы и вы получите нестабильные тесты без явной ошибки registry.
Переменные окружения и команды (копируйте в CI)
В начале install; URL — по allowlist security.
# Трансграничный pull: npm-стиль читают и npm, и Bun
export NPM_CONFIG_REGISTRY="https://registry.npmjs.org/"
export NPM_CONFIG_FETCH_RETRIES="3"
export NPM_CONFIG_FETCH_RETRY_MINTIMEOUT="20000"
export NPM_CONFIG_FETCH_RETRY_MAXTIMEOUT="120000"
export NPM_CONFIG_MAXSOCKETS="12"
export BUN_INSTALL_CACHE_DIR="${CI_CACHE_DIR:-$HOME/.cache/bun}/install"
export npm_config_cache="${CI_CACHE_DIR:-$HOME/.cache}/npm"
bun --version
bun install --frozen-lockfile
# Пример npm-only поддерева:
# (cd packages/legacy-npm && npm ci --omit=dev)
bunfig.toml без секретов:
# bunfig.toml — пример [install] # registry = "https://your.approved.mirror.example/npm" frozenLockfile = true # ignoreScripts = true # в CI при жёсткой политике скриптов
- Один URL в env,
.npmrcи при необходимостиbunfig.toml. - Приватные scope — токен из CI, read-only PAT.
Фрагменты ключей кеша (GitHub Actions)
Раздельные кеши — смена Bun.lockb не должна сбрасывать npm-кеш без причины.
- uses: actions/cache@v4
with:
path: ~/.bun/install/cache
key: bun-${{ runner.os }}-${{ hashFiles('bun.lockb', 'bun.lock') }}-${{ hashFiles('**/package.json') }}
restore-keys: |
bun-${{ runner.os }}-${{ hashFiles('bun.lockb', 'bun.lock') }}-
- uses: actions/cache@v4
with:
path: ~/.npm
key: npm-${{ runner.os }}-node${{ matrix.node }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
npm-${{ runner.os }}-node${{ matrix.node }}-
Оболочка с ограниченным повтором (только сеть)
Retry только сеть; по frozen/checksum — выход.
#!/usr/bin/env bash
set -euo pipefail
attempt=1 max=4 delay=2
while [ "$attempt" -le "$max" ]; do
log="bun-install-${attempt}.log"
if bun install --frozen-lockfile 2>&1 | tee "$log"; then exit 0; fi
if grep -qiE 'frozen.?lock|lockfile|checksum|integrity|ERESOLVE' "$log"; then exit 1; fi
sleep "$delay"; delay=$((delay * 2)); attempt=$((attempt + 1))
done
exit 1
FAQ
Можно ли разработчикам чередовать bun install и npm install в корне? Нет. Один инструмент обновляет lock в повседневной работе; второй — только в документированном окне обслуживания с двойным ревью.
Снимает ли быстрое зеркало необходимость капа параллелизма? Нет. Зеркала всё равно режут по rate limit; кап защищает общий egress пула удалённого Mac и стабилизирует p95 очереди.
Смежные темы? Yarn Berry, Deno/JSR.
Итог
Стабильность: один владелец lock на корень, одно зеркало, кап сокетов и job’ов, ключи кеша по lock и версиям инструментов, без retry на integrity.
CTA: macpull.com — главная без входа; тарифы, покупка, помощь; каталог блога для соседних матриц по JS.