ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

pnpm `--frozen-lockfile` 宽容策略:当锁文件固定了“外来“ package manager 条目时不再误报失败

pnpm `--frozen-lockfile` 宽容策略:当锁文件固定了“外来“ package manager 条目时不再误报失败 pnpm--frozen-lockfile宽容策略当锁文件固定了外来 package manager 条目时不再误报失败【免费下载链接】pnpmFast, disk space efficient package manager项目地址: https://gitcode.com/gh_mirrors/pn/pnpm导读本文围绕 pnpm 仓库中一条 patch 级变更记录changeset展开pnpm install --frozen-lockfile不再因为pnpm-lock.yaml中记录了当前运行中的 pnpm 并不会安装它的 package manager 引擎包条目而失败。文章将结合仓库源码讲清楚 env lockfile 的packageManagerDependencies结构、固定版本校验的判定逻辑、--frozen-lockfile与--no-frozen-lockfile的分支行为以及对应的测试用例帮助读者理解哪种条目被容忍、哪种条目仍被拒绝的边界并给出可复现的实操方式。背景pnpm v11 的 env lockfile 与packageManagerDependencies在 pnpm v11 中pnpm-lock.yaml的首个 YAML 文档被用作env lockfile环境锁文件用来记录配置依赖configurational dependencies以及packageManager/devEngines引导依赖。其结构定义位于 env_lockfile.rs每个 importer 对应一个EnvImporterSnapshot其中configDependencies恒被序列化而packageManagerDependencies仅在存在时写出#[serde(default, skip_serializing_if Option::is_none)]。每个条目是一个SpecifierAndResolution记录{ specifier, version }两个字段。env 文档只使用根 importer键为.EnvLockfile::create()会将其 seed 进importers。文档的lockfileVersion固定为字符串9.0。# pnpm-lock.yaml 开头的 env 文档示意结构 lockfileVersion: 9.0 importers: .: configDependencies: # ... packageManagerDependencies: pnpm: specifier: ^12.0.0 version: 12.0.0 packages: # package manager 引擎包及其子依赖 snapshots: # ...环境锁文件的解析、合并与写入逻辑都集中在 env_lockfile.rsread()只读取首个 YAML 文档write()会保留已有的主文档并以其作为第二个文档拼接写出未变化的文档不会被重写。问题来源pnpm 从哪些包安装自己随版本而变pnpm_engine_packages()位于 resolve_package_manager_integrities.rs根据要固定的 pnpm 版本决定引擎包集合版本 6.17.1且 12同时发布 JS 版pnpm与原生版pnpm/exe两个包都会被固定PACKAGE_MANAGER_DEPS_WITH_EXE版本 12只发布原生可执行文件本身仅固定pnpmPACKAGE_MANAGER_DEPS_PNPM_ONLY更早的版本同样只固定pnpm。由此产生一个真实的团队协作场景某位同事用 pnpm 11.xv11 会为 v12 版本固定pnpmpnpm/exe两个条目因为那是它自己这个 major 的安装来源提交了 lockfile而另一位同事用 pnpm 12.x 在 CI 上运行pnpm install --frozen-lockfile——12.x 只从pnpm安装自己pnpm/exe就成了一条当前 pnpm 不会安装它的引擎包条目。若严格执行记录内容必须与本次运行完全一致这类 lockfile 会被误判为过期并拒绝安装从而打断 CI。变更内容容忍多余引擎包拒绝别的版本变更记录 tolerate-a-foreign-package-manager-entry.md 描述的修复可拆成三个行为容忍本次修复pnpm-lock.yaml在packageManagerDependencies中记录了固定 pnpm 版本之外、但属于引擎包集合的条目例如 v12 运行时不使用的pnpm/exe--frozen-lockfile不再失败——前提是这些条目仍然指向当前想要固定的版本仍拒绝任何条目固定了其他版本的frozen 安装依然报错普通安装重写在可写锁文件的普通安装pnpm install中该块会被重写为当前这个 pnpm 实际安装来源的包集合。源码实现pins_wanted_package_manager的判定逻辑修复的核心在 resolve_package_manager_integrities.rs 的pins_wanted_package_manager()注释明确写了设计意图一个低于 11.20.0 的 pnpm 会在 v12 版本旁同时固定pnpm/exe……这样的条目通过同一份 integrity 固定了想要的版本且无法改变运行的是哪个 pnpm因此 frozen 锁文件接受它而不是让一个队友用旧 pnpm 最后写入的锁文件导致项目失败。判定分两步recorded_package_manager_deps()从 env 锁文件的根 importer 取出packageManagerDependencies映射不存在则返回false校验两个条件当前 pnpm 引擎包集合中的每个名字都出现在记录中package_manager_deps.iter().all(|name| pm_deps.contains_key(*name))记录的所有条目版本都等于固定版本并且该版本的包在packages中存在dep.version pnpm_version package_manager_entry_exists(...)。注意这里只要求包含想要的包 版本一致并不要求记录集合与引擎包集合大小完全相等——正是这一点容忍了多余的pnpm/exe条目。而is_package_manager_resolved_with_deps()同文件第 224-238 行则更严格它还要求记录集合的长度等于引擎包集合的长度且 specifier 完全一致用于走已记录条目快路径时的判断。分支走向同文件resolve_package_manager_integrities()第 44-83 行不强制重同步且已解析 → 直接返回现有 env 锁文件快路径opts.frozen_lockfile !force_resync→ 调用frozen_lockfile_result()若pins_wanted_package_manager通过则接受现有条目否则返回ERR_PNPM_FROZEN_LOCKFILE_WITH_OUTDATED_LOCKFILE错误消息为Cannot update packageManagerDependencies with frozen-lockfile because the lockfile is not up to date见 errors.rs普通安装 → 走完整解析流程把package_manager_dependencies写回 env 锁文件即重写该块。另外force_resync路径还有一个细节若强制重同步发生在 frozen 模式下repair_in_memory修复会在内存中重新解析并校验磁盘上的锁文件保持原样不动——因为这类条目其实已经记录了固定版本只是形式不被当前 pnpm 认可例如携带早期 pnpm 写下的 tarball URL不需要改动磁盘文件。测试验证三类场景全覆盖环境安装器的测试 tests/lockfile.rs 完整覆盖了上述三种行为测试场景期望结果记录中出现额外条目pnpm/exe固定到别的版本 11.23.0运行的是 v12仍报FrozenLockfileOutdated且磁盘锁文件字节不变记录缺失、或记录固定到其他版本12.0.0 → 13.0.0 的 bumped pin同样报FrozenLockfileOutdated锁文件未被写入env 锁文件已是最新frozen 安装成功强制重同步 frozenforce_resync_under_frozen_lockfile_resolves_without_writing在内存中修复解析磁盘锁文件字节不变其中 an entry pinning another version is an outdated lockfile 测试第 160-196 行正是仍被拒绝分支的直接证据它在已有记录里手工插入一个版本不匹配的pnpm/exe条目后调用resolve_package_manager_integrities断言返回ConfigDepError::FrozenLockfileOutdated且锁文件内容前后一致。实操指南命令、配置与错误处理命令行参数--frozen-lockfile及配套参数定义在 arguments.rs# 既不重新解析也不写入 pnpm-lock.yaml锁文件与 manifest 不一致时失败 pnpm install --frozen-lockfile # 显式允许更新锁文件覆盖配置中的 frozenLockfile: true pnpm install --no-frozen-lockfile # 默认开启true锁文件满足依赖时执行 headless 安装跳过解析 pnpm install --prefer-frozen-lockfile pnpm install --no-prefer-frozen-lockfile参数通过overrides_with成对互斥--frozen-lockfile与--no-frozen-lockfile后者覆盖前者遵循 CLI 优先于配置的惯例。配置文件项对应的 npm 配置项为frozenLockfile见 settings.rsNone默认即未配置与显式false--no-frozen-lockfile被区分对待以便两者按CLI 覆盖配置的通常顺序叠加语义install既不重新解析也不写入pnpm-lock.yaml锁文件与 manifest 过期时失败。与之相关的还有preferFrozenLockfile默认true见同文件第 446-447 行当现有锁文件满足 package.json 依赖时执行 headless 安装跳过全部依赖解析frozenLockfile: true时则强制执行失败语义prefer-frozen只是性能优化路径。二者的差异也反映在--frozen-lockfile与--prefer-frozen-lockfile两个参数上。遇到失败时怎么办当 frozen 安装因packageManagerDependencies过期而失败ERR_PNPM_FROZEN_LOCKFILE_WITH_OUTDATED_LOCKFILE时通常意味着 lockfile 与 manifest 确实不一致例如packageManager字段被升级。修复方式与常规 outdated lockfile 一致# 用非 frozen 模式安装一次让 pnpm 重写 packageManagerDependencies 块 pnpm install # 或将锁文件清理后重新生成 pnpm clean --lockfile pnpm install边界与设计取舍小结容忍的是多余但同版本的引擎包条目而不是任意差异校验仍然保证每个引擎包名字都存在、版本精确匹配因此被容忍的 lockfile 不可能暗中改变将要运行的 pnpm。拒绝的是版本不一致或缺失条目这正是--frozen-lockfile存在的意义——锁文件与 manifest 冲突时必须显式失败而不是静默改写。普通安装会重写该块把packageManagerDependencies收敛为当前 pnpm 实际安装来源的集合如 v12 下仅保留pnpm从而消除下次 frozen 运行中可能的噪音。这一修复让团队成员使用不同 pnpm 主版本、但共享同一份 lockfile的协作模型不再被误报中断同时保留了 frozen 锁文件作为可信事实来源的强度版本一致性的底线没有被放松只是不再苛求记录集合与本次运行集合的逐字节一致。【免费下载链接】pnpmFast, disk space efficient package manager项目地址: https://gitcode.com/gh_mirrors/pn/pnpm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表