为什么"npm install"重写package-lock.json?

Vip*_*ley 536 node.js npm npm-install 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真正尊重我的锁文件?

jot*_*aen 382

更新3:正如其他答案所指出的那样,该npm ci命令在npm 5.7.0中引入,作为在CI上下文中实现快速且可重现的构建的另一种方式.有关详细信息,请参阅文档npm博客.


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


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


原始答案:问题#16866中讨论的npm 5.1.0中的行为package-lock.json发生了变化.您观察到的行为显然是从版本5.1.0开始的npm.

这意味着只要找到一个新版本的依赖项,package.json就可以胜过.如果要有效地固定依赖关系,现在必须指定没有前缀的版本,例如,您需要将它们编写为而不是或.然后,和的组合将产生可重复的构建.要明确:单独不再锁定根级依赖项!package-lock.jsonpackage.json1.2.0~1.2.0^1.2.0package.jsonpackage-lock.jsonpackage-lock.json

无论这个设计决定是否合适都是有争议的,因此在问题#17979中对GitHub的混淆导致了持续的讨论.(在我看来,这是一个值得怀疑的决定;至少这个名字lock不再适用.)

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

  • 真是一团糟!世界上最大的软件包管理器,但它没有关于它应该如何工作的文档.每个人都在猜测它应该做什么,它变成了一场意见之战.讨论是好的,但应该在释放到野外之前发生.在某些时候,某人需要进行最后的调用,然后才能实现,记录和发布.PHP是由委员会设计的,并且是ad-hoc'd一起,看看结果如何.我不愿意看到同样的事情发生在这个关键且广泛使用的工具上. (229认同)
  • 那么,使用package-lock有什么意义呢?我认为它会在不同的工作空间中创建相同的环境但事实证明它什么都不做 (74认同)
  • 什么黑客是'npm update`呢?:○我有同样的感觉,`NPM install`更新DEPS,但我并不想相信它..但好像它是可悲的是真实的.不管怎么样还是有选择使用`NPM shrinkwrap`锁定DEPS,但肯定名称package-lock是不正确的,因为它不会冻结,也不会锁定依赖项. (36认同)
  • "然后,package.json和package-lock.json的组合将产生可重现的构建." "package-lock.json"在这里有什么作用?如果没有使用版本前缀,单独"package.json"是否已经产生可重现的构建? (14认同)
  • @JānisElmeris我认为package.json无法锁定深度依赖关系...... (9认同)
  • @LandonPoch不确定您对PHP的评论是指什么,但是Composer是一个比NPM(和yarn)更好的软件包管理器。听起来您可能有一段时间没有签出PHP了。 (5认同)
  • 纱线没有这个问题。它会完全安装package-lock中列出的内容。 (3认同)
  • 我真的很困惑......那么,1)我应该如何将完全锁定的包树部署到分段?只是"npm i --no-save"?2)如何在dev中更新软件包以准备新版本?"npm up --save"?老实说,我预计会有一个单独的命令从package-lock.json文件生成树 (2认同)
  • 这是否意味着“npm ci”的行为现在与“npm i”类似 - 至少在 package.json 未更改时对 package-lock.json 的修改方面是这样? (2认同)
  • @LandonPoch 该委员会负责 PHP7,这对 PHP 来说是一个巨大的进步。 (2认同)

Iva*_*kov 145

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

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

它的工作原理是丢弃你的node_modules并从头开始重新创建它.

除了保证你只获得锁文件中的内容之外,当你没有以node_modules开头时,它也比npm安装快得多(2x-10x!).

正如您可能从名称中获取的那样,我们希望它对持续集成环境来说是一个巨大的好处.我们还期望那些使用git标签进行生产部署的人将获得重大收益.

  • 如果存在锁定文件,这应该是默认行为. (114认同)
  • 所以他们改变了我的工作方式,只是为了几个月后才把它带回来? (11认同)
  • 我还是很困惑。在该项目中运行命令 `npm ci` 之前,文档说*“确保你有一个包锁和一个最新的安装:`npm install`”*。`npm install` 不会覆盖 package-lock.json 文件吗? (4认同)
  • AFAIK:@adiga - 从版本 5.4 开始,“npm”*仅*更改锁定文件*如果需要这样做,以满足packages.json*中的规范。因此,如果包过去说“thatpackage:1”,而锁说“..:1.0.4”,开发人员可以编辑说“thatpackage:2” - 这将强制锁定文件更改,因为“1.0.4”与新指定的范围不兼容。如果不更改 `packages.json`,将保持锁定在确切的版本,直到删除锁定文件。[如果没有保持锁定状态,并且没有更改packages.json,请提交错误报告。] (3认同)
  • 我花了一整天的时间在上面。我花了一整天的时间来研究这个基本问题:( :( (2认同)

Gal*_*lit 84

使用新推出的

npm ci
Run Code Online (Sandbox Code Playgroud)

npm ci承诺给大型团队带来最大利益.让开发人员能够"签署"程序包锁定可以促进大型团队之间更有效的协作,并且能够准确安装锁定文件中的内容,有可能每月节省数十(如果不是数百)开发人员小时数,从而解放团队花更多的时间来建造和运送惊人的东西.

介绍npm ci更快,更可靠的构建

  • @ phouse512这是对的.我们几乎_only_使用`npm ci`,如果更新或安装新软件包,只使用`npm install`. (5认同)
  • 这对我来说似乎是对的吗?谁能确认吗? (3认同)
  • 太糟糕了,它*总是*删除现有的`node_modules`目录并在本地重建,即使那是一个空的但很重要的符号链接。:( (2认同)
  • @ToolmakerSteve 别屏住呼吸!我认为删除目录的内容会比仅仅删除目录慢很多。您必须枚举内容,然后向操作系统发出一系列删除命令,而不仅仅是一个删除命令。由于之前在 npm 上提出的性能问题以及使用“npm ci”的改进,我预计他们将非常不愿意引入任何可能降低相当不常见用例性能的东西。您可能想查看 https://pnpm.js.org/,尽管它利用硬链接来减少磁盘使用量。 (2认同)

Ahm*_*any 48

简答:

  • 当package-lock.json存在时,它会否决package.json
  • 当package.json被修改时,它会否决package-lock.json

这是一个可以解释事物的场景(使用NPM 6.3.0验证)

你在package.json中声明了一个依赖,如:

"depA": "^1.0.0"
Run Code Online (Sandbox Code Playgroud)

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

"depA": "1.0.0"
Run Code Online (Sandbox Code Playgroud)

几天后,一个较新的次要版本的"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)
Run Code Online (Sandbox Code Playgroud)

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

"depA": "^1.1.0"
Run Code Online (Sandbox Code Playgroud)

然后重新运行:

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)
Run Code Online (Sandbox Code Playgroud)

  • 这确实是“锁定”文件的预期行为。显然,较旧版本的NPM并非如此。 (4认同)
  • @LahiruChandima它并不真正跟踪更新。除非npm install不满足package.json的要求,否则它将使用package-lock.json的锁定版本,在这种情况下,它将安装package.json并相应地重建package-lock.json。如果您以某种方式更改了package.json,以使现有的package-lock仍然满足更新后的package.json,它将继续使用该package-lock (3认同)

Dan*_*non 14

使用npm ci命令而不是npm install.

"ci"代表"干净安装".它将基于package-lock.json文件而不是lenient package.json文件依赖项来安装项目依赖项.

它将为您的其他队友提供相同的构建,并且速度也更快.

  • “ci”实际上代表“全新安装”。 (4认同)
  • “而且它也快得多” - 它将删除 `node_modules` 文件夹并从头开始重新创建它。真的快很多吗?`npm install` 也会删除 `node_modules` 文件夹吗? (3认同)
  • 我认为速度来自于 npm 不需要计算要下载哪些包。可以将其想象为“npm install”在运行时必须解决所有包依赖项。`npm ci` 只是一个“获取这些确切模块”的购物清单。 (3认同)
  • ci是指“持续集成”,如在文档和博客文章中宣布该命令的内容所提及:http://blog.npmjs.org/post/171556855892/introducing-npm-ci-for-faster-more-reliable (2认同)

Dan*_*non 7

看来此问题已在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 是现在安装现有项目时应使用的命令。

  • 我正在使用5.4.2,它仍然导致在`npm i`时对package-lock.json的修改。例如,当我在不支持fsevents的机器上执行npm i时,将删除模块fsevents,然后当在不支持fsevents的计算机上再次执行npm i时,将模块重新添加。 (5认同)

Tim*_*tom 7

将来,您将能够使用--from-lock-file(或类似)标志package-lock.json不修改它的情况下安装.

这对于可重复构建很重要的CI等环境非常有用.

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

  • @YevgeniyAfanasyev而不是那个标志,它被实现为`npm ci`,它也处理你的问题. (3认同)

Mat*_*att 5

你可能有类似的东西:

"typescript":"~2.1.6"
Run Code Online (Sandbox Code Playgroud)

在您的package.json哪个npm更新到最新的次要版本,在您的情况下2.4.1

编辑:来自OP的问题

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

回答:

这旨在锁定完整的依赖关系树.让我们说typescript v2.4.1需要widget ~v1.0.0.当你npm安装它抓住widget v1.0.0.稍后您的开发人员(或CI版本)会执行npm安装并获取typescript v2.4.1widget已更新为widget v1.0.1.现在您的节点模块不同步.这是package-lock.json阻止的.

或者更一般地说:

举个例子,考虑一下

包A:

{"name":"A","version":"0.1.0","依赖项":{"B":"<0.1.0"}}

包B:

{"name":"B","version":"0.0.1","依赖项":{"C":"<0.1.0"}}

和包C:

{"name":"C","version":"0.0.1"}

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

A@0.1.0 - B@0.0.1 - C@0.0.1

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

假设新版本没有修改B的依赖关系,则为A@0.1.0 - B@0.0.2 - C@0.0.1.当然,新版本的B可能包含新版本的C和任意数量的新依赖项.如果不希望这样的改变,A的作者可以指定对B@0.0.1的依赖.但是,如果A的作者和B的作者不是同一个人,那么A的作者就没有办法说当B完全没有改变时他或她不想拉入新出版的C版本.


OP问题2:让我看看我是否理解正确.您所说的是锁文件指定了辅助依赖项的版本,但仍然依赖于package.json的模糊匹配来确定顶级依赖项.那是准确的吗?

答:没有.package-lock锁定整个包树,包括中描述的根包package.json.如果typescript被锁定2.4.1在你的位置package-lock.json,它应该保持这种状态直到它被改变.并且让我们说明天typescript发布版本2.4.2.如果我签出你的分支并运行npm install,npm将尊重lockfile并安装2.4.1.

更多信息package-lock.json:

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

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

描述依赖关系树的单个表示,以确保队友,部署和持续集成能够安装完全相同的依赖关系.

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

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

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

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

  • 但这并不能解释为什么"npm install"会改变锁定文件.锁文件不是要创建可重现的构建吗?如果是这样,无论semver值如何,它仍应使用相同的2.1.6版本. (28认同)
  • 我不明白.如果后续安装可能仍然进行升级,这是一个"锁定"文件?! (15认同)
  • 我遇到过同样的问题.在我们的CI/CD中,`package-lock.json`被拉下来然后我们运行`npm install`,但是`package-lock.json`文件被修改了,我们必须执行重置才能拉出接下来的变化 (5认同)
  • 我认为他们开始时想把这个文件作为"信息"和"锁定",然后决定它只是一个"信息"文件.更好的名字是"package-info.json".我希望有一个"npm install -lock",它将从"package-lock.json"安装并忽略"package.json" (5认同)
  • 这就是我说的话.我的包锁文件说typescript@2.1.6但是当我运行npm install时,该条目被替换为typescript@2.4.1. (3认同)
  • 对我来说,这个答案并没有真正解决问题.特别是OP2的答案我无法重现; 事实上,npm 5.3.0在我的机器上的行为并不像这样.比方说,如果在`package.json`中使用`~2.1.6`指定了typescript,npm仍然会忽略锁文件中输出的内容. (2认同)

Sen*_*hik 5

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

npm ci
Run Code Online (Sandbox Code Playgroud)

npm install 如果您不想更改包的版本,则不要使用。

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

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