NodeJS的lock file及其使用

什么是lock file

lock file文件描述了整颗依赖树,包含了特定版本的传递依赖(依赖嵌套)关系。npm中用的是package-lock.json,yarn中用的是yarn.lock。

package-lock.json如下所示:

1.png

yarn.lock如下所示:

2.png

lockfile包含的关键信息包括

  • 安装每个依赖的实际版本
  • 每个依赖的依赖(即传递依赖)
  • 包的校验和(以验证包的完整性)

使用lockfile的方法如下:

npm ci # will install exactly what's in the package-lock.json
yarn install --frozen-lock-file # will install exactly what's in yarn.lock without updating it

什么时候使用lockfile

当我们在构建一个web应用时,建议使用上述的命令,来执行构建和发布。包括在CI中使用上述命令。这样我们可以确保每个开发验证人员、构建系统/CI系统使用完全相同的依赖项。

因此,在yarn和npm的文档里都有建议,在提交代码的时候,把lockfile也提交进GIT仓作为代码的一部分。

这也有利于可重复构建。对外交付时,任何时候都可以通过相同的代码构建出一致的包,客户也易于理解与验收。

什么时候不使用lockfile

当我们代码的构建结果是一个中间依赖,即被其他项目依赖时,不建议使用。换言之,lockfile应该进入顶层项目(最终用户消费的程序)的源码版本控制。

为什么被依赖件的源码仓不建议使用lock file呢?

主要原因在于npm仓库上发布的包本身。即,你发布到npm的内容并不总是与git仓上的内容相同。

npm使用npm pack命令将需要发布的文件打成一个tarball,你可以尝试使用如下命令来查看npm打包了哪些文件

npm pack --dry-run

也可以在 npm的doc 查看打包的完整文件列表,摘录其打包的内容如下

Certain files are always included, regardless of settings:

  • package.json
  • README
  • CHANGES / CHANGELOG / HISTORY
  • LICENSE / LICENCE
  • NOTICE
  • The file in the "main" field

README, CHANGES, LICENSE & NOTICE can have any case and extension.

Conversely, some files are always ignored:

  • .git
  • CVS
  • .svn
  • .hg
  • .lock-wscript
  • .wafpickle-N
  • .*.swp
  • .DS_Store
  • ._*
  • npm-debug.log
  • .npmrc
  • node_modules
  • config.gypi
  • *.orig
  • package-lock.json (use npm-shrinkwrap.json if you wish it to be published)

可以看到,npm只打包了package.json,而没有打包package-lock.json。

这种情况下,当别人的工程依赖你的npm包的时候,无法下载到你的工程的package-lock.json,导致实际依赖的你的包的传递依赖(即你所依赖的包)的版本不一定与你发布时的一致。

所以,作为一个被别人依赖的模块,关键在于让自己“靠谱”一些,而这点并不依靠lock文件。建议的“靠谱”方法包括:

  1. 严格遵循 semver 语义化版本的原则来进行版本发布。对不遵循 semver 发布的模块敬而远之。
  1. 选择依赖时,尽量选择npm 上有较大的下载量的,当遇到模块问题时,波及范围越广,其修复的速度越快。
  1. 开源模块在github 上的问题反馈迅速,或者是由一些知名开发者维护。质量有一定的保障
  1. 版本号中,patch 位变更的发布不多(说明 bug fix 不多)。

有的人会认为,即使如此,仍然应该把package-lock文件归档到git仓,哪怕它实际没有被使用。毕竟要使用lockfile都是单独的命令,如果不使用的话,归档也没什么影响。但是,事实真的如此吗?

首先,npm包管理是使用的semver 语义化版本的机制来帮助开发者管理依赖,开发者可以在 package.json中通过 ^1.1.0 或者 ~1.0.0 的方式来引入模块,如果开发者信任他们依赖的模块,开发者可以通过 ^ 来锁定一个模块的大版本,这样在每次重新安装依赖或者打包的时候,都能够享受到这个包所有的新增功能和 bug 修复。而这个模块如果遵循 semver 原则,也不用担心它会引入一些不兼容变更导致项目出现一些未知异常。最终开发者需要关心的其实只有直接依赖的这些模块是否足够靠谱。 这样一来,每个模块对自身的依赖负责,一个项目虽然只直接依赖了十来个模块,但其最终却间接的依赖了上千个模块。真正想要通过package-lock.json去管理好这一份多达上千个模块的模块是非常困难的。(一个关于lockfile成为安全侵入的入口的例子见这里https://snyk.io/blog/why-npm-lockfiles-can-be-a-security-blindspot-for-injecting-malicious-modules/,例子中,正是由于lockfile动辄几百上千行的修改,对commit审核造成了很大的压力,而通常一个commit也就几十行的修改。)

其次,在有lockfile文件的情况下,npm install的真正行为情况如下:

1、npm 5.0.x 版本,不管package.json怎么变,npm install 时都会根据lock文件下载

2、5.1.0 - 5.4.2版本 npm install 会无视lock文件,去下载符合规则的最新的包。

3、5.4.2版本后,如果改了package.json,且package.json和lock文件不同,那么执行npm install时npm会根据package.json中的版本号去下载最新的包,并更新lock文件。如果没有更新package.json,那么npm install会根据lock文件下载,而不会理会package.json中实际的包的版本是否有更新。

换句话说,当存在lockfile的情况下,即使有新的补丁包存在,都可能不会被使用 。而这点显然不是我们希望的

最后,重申一遍,lockfile应该进入顶层项目(最终用户消费的程序)的源码版本控制,中间模块不建议使用lockfile。

P.S:有人会认为开源社区大多用了lock。其实仔细看github上的提交就会发现,lock文件很多是不允许个人提交的,lock文件的更新大多由机器人自动更新,也就是说,这里的lock文件,是作为一个CI快照更新上库的。同样的,也有不少高星的开源社区,例如ESlint,库上没有lock文件。

你可能感兴趣的:(NodeJS的lock file及其使用)