繁体   English   中英

为什么“npm install”会重写package-lock.json?

[英]Why does “npm install” rewrite package-lock.json?

我最近刚刚升级到npm@5 我现在有一个package-lock.json文件,其中包含package.json 中的所有内容。 我希望,当我运行npm install ,将从锁定文件中提取依赖项版本以确定应该在我的node_modules目录中安装什么。 奇怪的是,它实际上最终修改和重写了我的package-lock.json文件。

例如,锁定文件的 typescript 指定为版本2.1.6 然后,在npm install命令之后,版本更改为2.4.1 这似乎违背了锁定文件的全部目的。

我错过了什么? 如何让 npm 真正尊重我的锁定文件?

更新 3:正如其他答案所指出的, npm ci命令在 npm 5.7.0 中被引入,作为在 CI 上下文中实现快速和可重复构建的额外方法。 有关更多信息,请参阅文档npm 博客


更新 2:更新和澄清文档的问题GitHub 问题 #18103


更新 1:下面描述的行为在 npm 5.4.2 中得到修复:当前预期的行为在GitHub 问题 #17979 中概述。


原始答案: package-lock.json的行为在npm 5.1.0 中已更改,如issue #16866 中所述 您观察到的行为显然是 npm 从 5.1.0 版开始的。

这意味着只要在package.json为依赖项找到更新版本, package.json就可以覆盖package-lock.json 如果您想有效地固定您的依赖项,您现在必须指定不带前缀的版本,例如,您需要将它们写为1.2.0而不是~1.2.0^1.2.0 然后package.jsonpackage-lock.json的组合将产生可重现的构建。 需要明确的是:单独的package-lock.json不再锁定根级依赖项!

这个设计决定是否好是有争议的,由于这种混淆在 GitHub 上的issue #17979上有一个持续的讨论。 (在我看来,这是一个有问题的决定;至少名称lock不再适用。)

另一个注意事项:对于不支持不可变包的注册表也有限制,例如当您直接从 GitHub 而不是 npmjs.org 拉取包时。 有关进一步说明,请参阅此包锁文档

我发现将有一个新版本的 npm 5.7.1带有新命令npm ci ,它将仅从package-lock.json安装

新的 npm ci 命令仅从您的锁定文件安装。 如果你的 package.json 和你的锁文件不同步,那么它会报告一个错误。

它的工作原理是扔掉你的 node_modules 并从头开始重新创建它。

除了保证您只会获得锁定文件中的内容之外,当您不从 node_modules 开始时,它也比 npm install 快得多(2x-10x!)。

正如您可能从名称中了解到的那样,我们希望它对持续集成环境有很大的帮助。 我们还希望从 git 标签进行生产部署的人会看到重大收益。

使用新推出的

npm ci

npm ci 承诺对大型团队最大的好处。 让开发人员能够“签署”一个包锁可以促进大型团队之间更有效的协作,并且能够准确安装锁文件中的内容有可能每月节省数十甚至数百个开发人员小时,从而释放团队花更多的时间来构建和运送令人惊奇的东西。

引入npm ci以实现更快、更可靠的构建

简答:

  • 只有当 package-lock.json 满足 package.json 的要求时, npm install才会使用它。
  • 如果它不满足这些要求,则更新包并覆盖包锁。
  • 如果您希望安装失败而不是在发生这种情况时覆盖包锁,请使用npm ci

这是一个可以解释事情的场景(已通过 NPM 6.3.0 验证)

您在 package.json 中声明一个依赖项,例如:

"depA": "^1.0.0"

然后你做, npm install这将生成一个 package-lock.json :

"depA": "1.0.0"

几天后,发布了一个更新的“depA”次要版本,比如“1.1.0”,那么以下内容成立:

npm ci       # respects only package-lock.json and installs 1.0.0

npm install  # also, respects the package-lock version and keeps 1.0.0 installed 
             # (i.e. when package-lock.json exists, it overrules package.json)

接下来,您手动将 package.json 更新为:

"depA": "^1.1.0"

然后重新运行:

npm ci      # will try to honor package-lock which says 1.0.0
            # but that does not satisfy package.json requirement of "^1.1.0" 
            # so it would throw an error 

npm install # installs "1.1.0" (as required by the updated package.json)
            # also rewrites package-lock.json version to "1.1.0"
            # (i.e. when package.json is modified, it overrules the package-lock.json)

使用npm ci命令而不是npm install

“ci”代表“持续集成”。

它将安装基于 package-lock.json 文件而不是 lenient package.json 文件依赖项的项目依赖项。

它将为您的队友生成相同的构建,而且速度也快得多。

您可以在这篇博文中阅读更多相关信息: https : //blog.npmjs.org/post/171556855892/introducing-npm-ci-for-faster-more-reliable

看来这个问题已在 npm v5.4.2 中修复

https://github.com/npm/npm/issues/17979

(向下滚动到线程中的最后一条评论)

更新

实际上已在 5.6.0 中修复。 5.4.2 中存在一个跨平台错误,导致该问题仍然存在。

https://github.com/npm/npm/issues/18712

更新 2

在此处查看我的答案: https : //stackoverflow.com/a/53680257/1611058

npm ci是您现在安装现有项目时应该使用的命令。

将来,您将能够使用--from-lock-file (或类似的)标志仅从package-lock.json安装而无需修改它。

这对于 CI 等环境中可重现的构建很重要的环境非常有用。

有关该功能的跟踪,请参阅https://github.com/npm/npm/issues/18286

可能你应该使用这样的东西

npm ci

如果您不想更改软件包的版本,请不要使用npm install

根据官方文档, npm installnpm ci安装了项目所需的依赖项。

主要区别在于, npm install确实安装了以packge.json为参考的包。 npm ci的情况下,它确实以package-lock.json作为参考安装包,确保每次安装准确的包。

你可能有这样的事情:

"typescript":"~2.1.6"

在你的package.json中 npm 更新到最新的次要版本,在你的情况下是2.4.1

编辑:来自 OP 的问题

但这并不能解释为什么“npm install”会更改锁定文件。 锁定文件不是用于创建可重现的构建吗? 如果是这样,无论 semver 值如何,它仍应使用相同的 2.1.6 版本。

回答:

这旨在锁定您的完整依赖树。 假设typescript v2.4.1需要widget ~v1.0.0 当您 npm install 时,它会抓取widget v1.0.0 稍后,您的开发人员(或 CI 构建)会安装 npm 并获取typescript v2.4.1widget已更新为widget v1.0.1 现在您的节点模块不同步。 这就是package-lock.json阻止的。

或更一般地说:

例如,考虑

套餐一:

{ "name": "A", "version": "0.1.0", "dependencies": { "B": "<0.1.0" } }

套餐B:

{ "name": "B", "version": "0.0.1", "dependencies": { "C": "<0.1.0" } }

和包C:

{“名称”:“C”,“版本”:“0.0.1”}

如果这些是注册表中唯一可用的 A、B 和 C 版本,则正常的 npm install A 将安装:

A@0.1.0 -- B@0.0.1 -- C@0.0.1

但是,如果发布了 B@0.0.2,则将安装新的 npm install A:

A@0.1.0 -- B@0.0.2 -- C@0.0.1 假设新版本没有修改 B 的依赖项。 当然,新版本的 B 可以包含新版本的 C 和任意数量的新依赖项。 如果此类更改不受欢迎,A 的作者可以指定对 B@0.0.1 的依赖。 但是,如果 A 的作者和 B 的作者不是同一个人,则 A 的作者无法在 B 根本没有改变的情况下说他或她不想引入 C 的新发布版本。


OP 问题 2:让我看看我是否理解正确。 你说的是lock文件指定了二级依赖的版本,但是还是依赖package.json的模糊匹配来确定顶级依赖。 那是准确的吗?

答:不会。 package-lock 会锁定整个包树,包括package.json描述的根包。 如果typescript在锁定2.4.1在您的package-lock.json ,直到它被改变了它应该保持这种方式。 假设明天typescript发布版本2.4.2 如果我检出您的分支并运行npm install ,npm 将尊重锁文件并安装2.4.1

更多关于package-lock.json

package-lock.json 会为任何 npm 修改 node_modules 树或 package.json 的操作自动生成。 它描述了生成的确切树,以便后续安装能够生成相同的树,而不管中间依赖项更新如何。

此文件旨在提交到源存储库中,并用于多种目的:

描述依赖树的单一表示,以便保证团队成员、部署和持续集成安装完全相同的依赖关系。

为用户提供一种“时间旅行”到 node_modules 以前状态的工具,而无需提交目录本身。

通过可读的源代码控制差异来促进树更改的更大可见性。

并通过允许 npm 跳过先前安装的软件包的重复元数据解析来优化安装过程。

https://docs.npmjs.com/files/package-lock.json

编辑:“锁”这个名字很棘手,它的 NPM 试图赶上 Yarn。 它不是一个锁定的文件。 package.json是用户固定的文件,一旦“安装”将生成 node_modules 文件夹树,然后该树将写入package-lock.json 所以你看,它的另一种方式 - 依赖版本将一如既往地从package.json提取,并且package-lock.json应该被称为package-tree.json

(希望这能让我的答案更清晰,经过这么多次投票后)


一个简单的答案: package.json像往常一样拥有您的依赖项,而package-lock.json是“一个精确的,更重要的是可重现的 node_modules 树”(取自npm 文档本身)。

至于这个棘手的名字,它的 NPM 试图赶上 Yarn。

在他们的 github 页面上有一个未解决的问题: https : //github.com/npm/npm/issues/18712

当开发人员使用不同的操作系统时,这个问题最为严重。

Npm install 检测对 package.json 文件所做的任何更改,以相应地反映依赖项列表。

前任。 如果用户添加或删除了新的依赖项,构建将下载或删除本地计算机中的依赖项。 我们可以将其与 java 中的 .m2 存储库进行比较,其中 maven 不断跟踪 pom.xml 文件以更新依赖项。

package-lock.json 是内部进程在运行时使用的 package.json 的副本,唯一不同的是 package-lock.json 对用户是只读的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM