若你在 npm workspaces 或 Turborepo 式 monorepo 里同时引入 Bun 做安装加速、又保留 npm 生态工具链,远程 Mac 共享 runner 上最常见的故障不是「装不上」,而是双锁漂移、registry 半套镜像、并发打满触发 429。本文不写 Git 子模块与 Docker 层缓存,只钉死 Bun.lockbpackage-lock.json 的验收节奏、跨境 registry 环境变量与缓存键。延展阅读:Yarn Berry 与 node_modules 的 registry 矩阵(另一套包管理器选型);站内导航:MacPull 首页技术博客列表均可免登录打开。

可执行决策矩阵(按行抄进 Runbook)

下列表格把「选型—参数—阈值」压成一行决策:默认以 Node 22+darwin arm64 为基线;若你的池子混跑 x64 Rosetta,请在缓存键中追加 arch 片段以免错命中。

若你是带着「Bun.lockb 能替代 package-lock 吗」「混仓先 bun 还是先 npm ci」「镜像环境变量怎么双写」「GitHub Actions 缓存键 hashFiles 怎么拼」这类长尾问句打开本文,可直接以表格最后一列「混仓推荐动作」为验收清单:每一项都应对应一条流水线日志或合并请求模板字段,避免口头约定。

维度 Bun.lockb(二进制锁) package-lock.json(npm v3 锁) 混仓推荐动作
真相源与 CI 门禁 bun install --frozen-lockfile;锁变更只能通过本地 bun install 再提交 npm cinpm install --package-lock-only 后 diff 门禁 在 ADR 写死「先跑哪条锁」;另一条锁的更新必须附带合并说明与双跑计时
registry 镜像(跨境) bunfig.tomlinstall.registry 或环境变量 BUN_CONFIG_REGISTRY NPM_CONFIG_REGISTRY.npmrcregistry= 同一镜像根双写;步骤开头 echo 打印两处解析结果
作用域与私库 bunfig.toml 使用 [install.scopes]@org 绑定到私库根;与 npm 的 @org:registry= 成对核对 //registry.npmjs.org/:_authToken 等(建议仅 CI 注入) 令牌只读、短 TTL;轮换后各跑一次 frozen / ci
并发安装 控制脚本并行度;避免与 bun install 同时再跑全量 npm ci 打双份流量 NPM_CONFIG_MAXSOCKETS=12 起评,429 降到 6 同一 Job 内串行化「锁安装」阶段,测试分片后置
缓存键命名(示例片段) bun-{{ hashFiles('Bun.lockb') }}-{{ runner.os }}-arm64-bun-$(bun --version) npm-{{ hashFiles('package-lock.json') }}-node-${{ env.NODE_VERSION }} 混仓用拼接:bunlock+plock+arch+bunVer+nodeVer,任一锁变则全量 miss
失败重试阈值 网络类:2s / 4s / 8s 指数退避,最多 3 轮;仍失败则降级为单线程重装 NPM_CONFIG_FETCH_RETRIES=3NPM_CONFIG_FETCH_RETRY_MINTIMEOUT=2000NPM_CONFIG_FETCH_RETRY_MAXTIMEOUT=8000 429 与 TLS reset 共用退避表;每轮记录 registry 主机名

可复制环境变量与命令骨架

将镜像域名替换为企业可审计端点;勿把长期 PAT 写进仓库。Apple Silicon runner 上建议显式限制旧空间占用:export NODE_OPTIONS=--max-old-space-size=8192 仍适用于跑在 Node 下的测试进程。

# 跨境 registry 与缓存目录(示例) export NPM_CONFIG_REGISTRY="https://registry.npmmirror.com" export BUN_CONFIG_REGISTRY="https://registry.npmmirror.com" export NPM_CONFIG_FETCH_RETRIES="3" export NPM_CONFIG_FETCH_RETRY_MINTIMEOUT="2000" export NPM_CONFIG_FETCH_RETRY_MAXTIMEOUT="8000" export NPM_CONFIG_MAXSOCKETS="12" export BUN_INSTALL_CACHE_DIR="${CI_CACHE_DIR:-$HOME/.cache/bun}/install" # 安装门禁(混仓:先 Bun 再 npm,或按 ADR 反转顺序,但禁止并行互踩) bun --version && node --version bun install --frozen-lockfile npm ci --no-audit --no-fund --prefer-offline # 可选:shell 级退避包装(Bun 侧无等价 npm 全量参数时) i=0; until bun install --frozen-lockfile; do i=$((i+1)); [ "$i" -ge 3 ] && exit 1; sleep $((2**i)); done

若子包目录单独维护锁文件,把 hashFiles 模式改为 **/Bun.lockb**/package-lock.json 或在矩阵 Job 里按 workspace 分键,避免「子锁已变、根键未变」的假命中。

lockfile 一致性验收(不写 SPM、不写 Docker)

Bun.lockb 为二进制,合并冲突时肉眼 diff 困难:请在 CI 增加「锁文件存在性 + frozen 安装」即可验证可复现性;代码评审依赖 bun lockfile 迁移工具或团队约定的 lock 导出流程。package-lock.json 适合在合并请求展示 npm ls --package-lock-only 的摘要。混仓反模式是本地用 Bun 改依赖却未更新 npm 锁,或反之;建议在 package.jsonpackageManager 字段声明主安装器,并在 pre-commit 或 CI 首步校验声明与真实命令一致。

精简 FAQ

能否只缓存 node_modules 不缓存 Bun 目录?

不推荐跨 Job 直接 tarball 整个 node_modules(路径与可选依赖平台片段易碎)。更稳的是缓存 BUN_INSTALL_CACHE_DIR 与 npm 的 cache root,再每次 frozen/ci 重放。

镜像返回的 tarball 与上游哈希不一致怎么办?

立即切换镜像线路或改透传代理;不要把「跳过完整性校验」写进默认流水线,远程共享机上的 supply-chain 风险会被放大。

总结:二零二六年要在远程 Mac CI上把 Bun 与 npm 混仓跑稳,先把双锁与双 registry 环境变量对齐,再用缓存键把「锁 + 架构 + 运行时版本」绑死,最后用三轮指数退避兜住跨境抖动。需要可预期出口与独占算力时,可打开 https://macpull.com/zh/index.html 了解方案,并直接访问 定价帮助中心购买页(均无需登录即可浏览)。

把远程 Mac 流水线交给可预期网络与磁盘治理

macpull.com 首页 进入产品与文档;套餐定价帮助博客列表免登录可达。