Turbo prune --docker 与 Bun 冻结锁文件不兼容:bun-v1-issue-12653 复现与根因剖析
构建工具开发工具CLI【免费下载链接】turboBuild system optimized for JavaScript and TypeScript, written in Rust项目地址https://gitcode.com/gh_mirrors/tu/turbo点击查看免费下载本文基于 turbo 仓库Build system optimized for JavaScript and TypeScript, written in Rust锁文件测试套件中的bun-v1-issue-12653复现工程完整还原turbo prune --docker生成的out/json/bun.lock在 Docker 构建中被bun install --frozen-lockfile拒绝的真实故障场景。读完本文你将掌握该 bug 的完整复现步骤、每条错误信息的含义、bun.lock 中版本作用域scoped entry导致解析失败的具体机理以及 turbo 仓库如何用锁文件 fixture 体系持续回归这类问题。一、背景turbo prune --docker与 Bun 锁文件的组合为何值得关注turbo prune是 turbo 提供的按目标工作区裁剪能力给定一个目标 workspace如eventmate/migrations它会递归计算出依赖它的全部 workspace 与外部依赖生成一个只包含必要文件的目录供 CI / Docker 构建使用。其中--docker模式会将输出拆分为两个目录out/json/裁剪后的package.json与锁文件供安装阶段使用out/full/裁剪后的完整源码供构建阶段使用。当 monorepo 使用 Bun 作为包管理器时out/json/中会出现裁剪后的bun.lock。安装阶段通常配合--frozen-lockfile保证可复现构建。问题恰恰出现在这里裁剪后的 bun.lock 可能是结构上合法、语义上不可解析的——这也是本文 fixture 要复现的核心故障。lockfile-tests/fixtures/bun-v1-issue-12653/README.md 明确写道该仓库intentionally keeps source code minimal while preserving the relevant workspacepackage.jsondependency graph from the original project即用最小化源码保留真实的依赖图以便稳定触发问题。二、复现工程解剖fixture 的目录结构与关键配置fixture 位于 lockfile-tests/fixtures/bun-v1-issue-12653/目标镜像是一个 Prisma 迁移服务migration servicelockfile-tests/fixtures/bun-v1-issue-12653/ ├── apps/ │ ├── admin|api|bot|docs|frontend|marketing/ # 各 package.json │ └── migrations/ # 目标工作区 │ ├── Dockerfile │ ├── docker-entrypoint.sh │ └── package.json ├── packages/ │ ├── audit|metrics|permissions|stripe-listener|ui/ │ ├── database/ │ │ ├── index.ts │ │ ├── package.json │ │ ├── prisma.config.ts │ │ └── prisma/{schema.prisma, migrations/} │ └── ... ├── bun.lock ├── bunfig.toml ├── meta.json ├── package.json └── turbo.jsonmeta.json驱动测试套件的元数据meta.json 声明了测试所需的全部关键信息{ packageManager: bun, packageManagerVersion: bun1.3.13, lockfileName: bun.lock, frozenInstallCommand: [ bun, install, --frozen-lockfile, --ignore-scripts ], pruneTargets: [eventmate/migrations] }pruneTargets指定裁剪目标为eventmate/migrations与 Dockerfile 中的turbo prune eventmate/migrations --docker一致frozenInstallCommand与 Dockerfile 安装阶段的命令完全对齐说明 fixture 刻意模拟真实 CI 的安装参数。bunfig.tomlhoisted 链接模式bunfig.toml 内容极简但关键[install] linker hoisted它强制 Bun 使用 hoisted提升链接模式。这会影响 bun.lock 中依赖的存放形态满足版本约束的依赖被提升到根级无法提升的版本则记录为路径作用域条目如eventmate/database/recharts/...。这是理解后文根因的重要前提。package.json依赖图的关键特征根 package.json 声明了packages/*与apps/*两个 workspace 通配包管理器为bun1.3.13并使用了 Bun Catalogcatalog:、catalogs:字段与overrides。真正把问题引出来的是 packages/database/package.json 中这一条dependencies: { prisma/adapter-pg: catalog:, prisma/client: catalog:, dotenv: catalog:, pg: catalog:, recharts: ^3.1.2 }而目标工作区 apps/migrations/package.json 依赖eventmate/databaseworkspace:*dependencies: { eventmate/database: workspace:*, dotenv: catalog: }于是裁剪eventmate/migrations会连带拉入eventmate/database进而拉入recharts^3.1.2——这个间接依赖正是崩溃的导火索。README 还特别说明Prisma 包的存在是为了匹配迁移服务的 Docker 镜像形态而--ignore-scripts用于在裁剪后的源码被拷贝进来之前阻止postinstallpackages/database/package.json 中有postinstall: bun run generate提前执行。三、复现步骤从 Docker 构建到安装阶段的失败完整复现命令README 原文docker build --no-cache -t turbo-bun-prune-frozen-repro -f apps/migrations/Dockerfile .Dockerfile 采用标准多阶段构建问题发生在installer阶段FROM oven/bun:1.3.13-alpine AS base WORKDIR /app FROM base AS pruner RUN bun add -g turbo2.9.7-canary.13 COPY . . RUN turbo prune eventmate/migrations --docker FROM base AS installer COPY --frompruner /app/out/json/ . RUN bun install --frozen-lockfile --ignore-scripts # ← 失败点 COPY --frompruner /app/out/full/ . RUN cd packages/database bun run generate FROM base AS runner COPY --frominstaller /app ./ RUN chmod x ./apps/migrations/docker-entrypoint.sh WORKDIR /app/apps/migrations CMD [./docker-entrypoint.sh]流程要点pruner阶段用bun add -g turbo2.9.7-canary.13安装 turbo 并执行turbo prune --docker产出out/json/含裁剪后的 bun.lock与out/full/installer阶段先拷贝out/json/随即执行bun install --frozen-lockfile --ignore-scripts预期结果该命令失败且失败发生在 Prisma generation 之前COPY --frompruner /app/out/full/ .之后才会执行bun run generate最终runner阶段通过 docker-entrypoint.sh 执行bun run migrate:deploy即 Prisma 迁移部署。四、失败现象逐条解读在oven/bun:1.3.13-alpine与turbo2.9.7-canary.13组合下观察到的完整错误输出为error: Failed to resolve prod dependency eventemitter3 for package recharts InvalidPackageInfo: failed to parse lockfile: bun.lock warn: Ignoring lockfile error: lockfile had changes, but lockfile is frozen输出行含义error: Failed to resolve prod dependency eventemitter3 for package rechartsBun 在解析裁剪后的 bun.lock 时发现recharts声明需要eventemitter3却无法在该锁文件中定位到满足约束的eventemitter3条目——这是真正的根因信号InvalidPackageInfo: failed to parse lockfile: bun.lock由于上述依赖解析失败bun 判定锁文件信息无效InvalidPackageInfo无法继续按锁文件执行安装warn: Ignoring lockfilebun 退而求其次选择忽略锁文件、按 package.json 现场解析error: lockfile had changes, but lockfile is frozen忽略锁文件意味着依赖解析结果必然与 bun.lock 不一致而--frozen-lockfile禁止任何变更最终以错误收场可见失败的靶心是第一条裁剪后的锁文件中recharts与eventemitter3之间的依赖记录对 bun 而言不可解析后续的InvalidPackageInfo/Ignoring lockfile/ frozen 报错都是连锁反应。五、对照组根锁文件本身是有效的为了证明问题出在裁剪而非锁文件本身README 给出了一个关键对照实验——直接用根目录的原始 bun.lock 执行相同的安装参数docker run --rm -v $PWD:/work -w /work oven/bun:1.3.13-alpine bun install --frozen-lockfile --ignore-scripts结论根锁文件有效该命令可以正常通过。失败只会在turbo prune --docker生成out/json/bun.lock之后出现。这一对照组设计思路在 turbo 的锁文件测试运行器中有对应实现doValidate会先在未裁剪的原始 fixture上执行一次包管理器校验只有原始 lockfile 与其 package.json 匹配才继续测试turbo prune的输出见 runners/local.ts。六、从 bun.lock 结构推断根因要理解裁剪为何会破坏解析需要看 bun.lock 中 recharts / eventemitter3 的记录形态以下行号基于当前仓库文件实测根级提升的 recharts2.15.4bun.lock#L3350依赖eventemitter3: ^4.0.1对应根级条目eventemitter34.0.7bun.lock#L2298路径作用域的 recharts3.8.1eventmate/database/recharts与eventmate/ui/recharts都解析到recharts3.8.1bun.lock#L3938、bun.lock#L3950它们的依赖是eventemitter3: ^5.0.1对应的 eventemitter35.0.4被记录为eventmate/database/recharts/eventemitter3bun.lock#L4724与eventmate/ui/recharts/eventemitter3bun.lock#L4736。可以推断的机理如下apps/migrations的裁剪会保留eventmate/database它是 workspace 依赖因此裁剪后的锁文件需要包含eventmate/database视角下的recharts即eventmate/database/recharts→ recharts3.8.1而recharts3.8.1 要求eventemitter3^5.0.1根级提升的eventemitter34.0.7只满足 recharts2.x 的^4.0.1无法满足^5.0.1满足要求的 5.x 条目eventmate/database/recharts/eventemitter3是随父包路径作用域记录的裁剪器在生成out/json/bun.lock时eventmate/ui被正确剪掉但eventmate/database/recharts/eventemitter3这类路径作用域条目与其父节点recharts的关联未被完整保留导致 bun 在解析 recharts 的依赖时找不到eventemitter3最终报出Failed to resolve prod dependency eventemitter3 for package recharts。这正是 hoisted linker 下根级版本 路径作用域版本并存的典型陷阱裁剪器对 bun.lock 的路径作用域条目scoped entries处理不完整生成的文件对 bun 而言是非法锁文件而非单纯的版本不满足。需要说明的是以上根因分析基于当前仓库 bun.lock 的结构推断fixture 本身定位为可稳定复现的失败样本该缺陷是否在后续 turbo / bun 版本中修复仓库内并未声明。七、该 fixture 在 turbo 锁文件测试体系中的角色bun-v1-issue-12653是 turbo 仓库lockfile-tests回归体系的组成部分。该体系的入口是 check-lockfiles.tsEnd-to-end validation that turborepos lockfile pruning produces lockfiles that package managers accept without downloading packages.其运行方式为将每个 fixture 拷贝到临时目录 → 对每个目标 workspace 执行turbo prune支持--docker/--production变体→ 用对应包管理器在裁剪产物目录校验锁文件。使用方式pnpm check-lockfiles # 全部 fixture pnpm check-lockfiles --fixture bun-v1-issue-12653 # 只跑该 fixture pnpm check-lockfiles --pm bun # 只跑 bun 系列 pnpm check-lockfiles --turbo-path ./path/to/turbo # 指定 turbo 二进制关键实现细节见 runners/local.tsbun 的校验命令是case bun: { return { command: bun install --frozen-lockfile, env: { BUN_CONFIG_SKIP_INSTALL_PACKAGES: 1 } }; }BUN_CONFIG_SKIP_INSTALL_PACKAGES1让 bun 只做锁文件解析校验、不真正下载安装包从而在 CI 中快速判断裁剪后的锁文件能否被 bun 接受。types.ts定义了PackageManagerType含bun、TestCase、TestResult等类型expectedFailures字段则允许显式声明已知会失败的 workspace。fixture 目录命名bun-v1-issue-12653遵循包管理器 版本 上游 issue 号的惯例——同目录下还有bun-v1-issue-10410、bun-v1-issue-12156、bun-v1-issue-12252等一系列 Bun 锁文件问题样本共同构成针对 Bun 生态的持续回归防线。八、排障与规避思路基于本次复现可以总结出几条可操作的排障路径先验证锁文件 vs 依赖图是否自洽如果bun install --frozen-lockfile报出Failed to resolve ... for package ...优先检查报错包这里是recharts在其所有作用域条目中声明的依赖版本是否都能在锁文件中找到对应条目区分原始锁文件有效与裁剪锁文件有效用docker run直接在根目录执行相同安装命令做对照即 README 的 Control 实验可以快速定位问题出在 prune 阶段检查out/json/的裁剪产物turbo prune --docker后查看out/json/bun.lock重点核对被保留包如eventmate/database/recharts/...的路径作用域条目是否完整、其依赖节点是否可达hoisted linker 下根级 4.x 路径级 5.x 并存的形态尤其容易在裁剪时丢条目结合--ignore-scripts的语义排查--ignore-scripts会跳过所有生命周期脚本本例中是 database 包的postinstall: bun run generate避免在源码拷贝进来前触发 Prisma 生成——若去掉该参数错误信息会被后置的生成失败掩盖干扰定位用最小 fixture 复现本 fixture 的做法值得借鉴——保留真实依赖图、删减无关源码并把目标镜像形态Prisma 迁移服务还原为最小 Dockerfile从而把复杂 monorepo 的构建失败压缩成可一键复现的最小样本。综上bun-v1-issue-12653是一个过程正确、结果非法的锁文件裁剪案例原始 bun.lock 完全有效turbo prune --docker也能正常产出但裁剪后的out/json/bun.lock丢失了recharts3.8.1所需的eventemitter3^5.0.1路径作用域条目最终被bun install --frozen-lockfile以InvalidPackageInfo frozen 冲突的形式拒绝。理解这一机理不仅能帮你排查同类 Bun Turbo Docker 构建问题也能更深刻地理解锁文件裁剪器在处理 hoisted linker 产物时面临的语义挑战。赞分享构建工具开发工具CLI【免费下载链接】turboBuild system optimized for JavaScript and TypeScript, written in Rust项目地址https://gitcode.com/gh_mirrors/tu/turbo点击查看免费下载相关推荐turbo prune 丢失 Yarn packageExtension 依赖边yarn install --immutable 在 CI 中失败的根因与完整复现turbo prune 丢失 Yarn packageExtension 依赖边 yarn install immutable 在 CI 中失败的根因与完整复构建工具开发工具CLIRenovate bun-version Manager自动维护 .bun-version 文件锁定 Bun 运行时版本Renovate bun version Manager自动维护 .bun version 文件锁定 Bun 运行时版本 导读 本文围绕 Renovate开发工具DevOps后端pnpm 过滤安装不再写坏锁文件--prod/--dev/prune 与 --frozen-lockfile 的兼容性修复pnpm 过滤安装不再写坏锁文件 prod / dev / prune 与 frozen lockfile 的兼容性修复 本篇文章围绕 pnpm 仓库中 .c包管理器开发工具CLI创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考