69

To lock the versions of dependencies that are installed over a project, the command npm install creates a file called package-lock.json. This was made since Node.js v8.0.0 and npm v5.0.0, as several of you might know.

Despite of Node.js and npm recommendations about committing this file, several concerns regarding when you should avoid to do it, are also an option. Typically we commit in our projects, nevertheless, it is a peculiar question.

While we should commit the package-lock.json file by default, we have a specific case we should not. For instance, if we want to test the latest version of our project dependencies, it can be an option to add package-lock.json into .gitignore.

So, the questions are as follows:

  1. Should the package-lock.json file be added to .gitignore?
  2. Is there any particular situation that we MUST or MUST NOT do it?
Francisco Maria Calisto
  • 1,578
  • 2
  • 15
  • 34

1 Answers1

124

No, the package-lock.json SHOULD NOT be added to .gitignore. Instead, I strongly advise:

  1. Add the package-lock.json you to your version control repository
  2. Use npm ci instead of npm install when building your application both locally and in your deployment pipeline.
    (The ci command is available since npm@5.7, if in doubt upgrade your npm via:
    npm install -g npm.)

One of the biggest downside of the npm install command is its unexpected behavior that it may mutate the package-lock.json, whereas npm ci only uses the version in the lockfile and produces an error if the package-lock.json and package.json are out of sync.

Also, npm ci requires the existence of a package-lock.json and would print an error if it wasn't there. There is a strong use-case for being able to trust that the project's dependencies resolve repeatably in a reliable way across different machines.

Furthermore, npm ci nukes the entire node_modules folder before adding the dependencies making sure you work with your actual dependencies instead of local changes while still being faster than a normal npm install.

From a package-lock.json you get exactly that: a known-to-work state.

In the past, I had projects without package-lock.json / npm-shrinkwrap.json / yarn.lock files whose build would fail one day because a random dependency got a breaking update. (While a lot of libraries respect the semvar versioning guideline, you have no guarantee they won't break on a minor upgrade.)

Those issue are hard to resolve as you sometimes have to guess what the last working version was.

In regards to testing the latest dependencies for your project: This is what npm update is for and I argue that it should be run by a developer, who also runs the test locally, who resolves issue if they may arise, and who then commits the changed package-lock.json. (If an upgrade fails, they can revert to the last working package-lock.json.)

Furthermore, I rarely upgrade all the dependencies at once (as that too might require further maintenance) but I rather cherry-pick the update I need (e.g. npm update {dependency}, or npm install {dependency}@2.1.3). Which is another reason why I would see it as a manual maintenance step.

If you really want to have it automated you could create a job for:

  • checkout repository
  • run npm update
  • run tests
    • if tests passes, then commit and push to repository
    • else fail and report issue to be manually resolved

This is something I would see hosted on a CI server, e.g. Jenkins, and it should not be achieved through aforementioned reason through adding the file to the .gitignore.


Or to quote npm doc:

It is highly recommended you commit the generated package lock to source control: this will allow anyone else on your team, your deployments, your CI/continuous integration, and anyone else who runs npm install in your package source to get the exact same dependency tree that you were developing on. Additionally, the diffs from these changes are human-readable and will inform you of any changes npm has made to your node_modules, so you can notice if any transitive dependencies were updated, hoisted, etc.

And in regards to the difference between npm ci vs npm install:

  • The project must have an existing package-lock.json or npm-shrinkwrap.json.
  • If dependencies in the package lock do not match those in package.json, npm ci will exit with an error, instead of updating the package lock.
  • npm ci can only install entire projects at a time: individual dependencies cannot be added with this command.
  • If a node_modules is already present, it will be automatically removed before npm ci begins its install.
  • It will never write to package.json or any of the package-locks: installs are essentially frozen.
k0pernikus
  • 41,137
  • 49
  • 170
  • 286
  • 1
    I disagree, though I use npm install, not npm ci. With npm install, having package-lock.json on the repo causes issues in deployment. see my answer on https://stackoverflow.com/questions/44206782/do-i-commit-the-package-lock-json-file-created-by-npm-5/54102769#54102769 – MagicLAMP Jan 10 '19 at 21:12
  • 3
    @MagicLAMP You rather should use `npm ci` instead of `npm install` on your build server. Then your problem would go away. – k0pernikus Aug 26 '19 at 08:11
  • 1
    Thanks @k0pernikus. I will try this in the week and comment again. – MagicLAMP Aug 28 '19 at 03:29
  • 1
    "npm ci" gives an error, where npm does not understand "ci" as a command. Is this shorthand for something. I tried 'c' on its own, and that did not work. I tried 'i' on its own and I think it just did an install – MagicLAMP Aug 28 '19 at 05:45
  • 1
    @MagicLAMP Check your npm version. `npm ci` is available since npm@5.7, current version is `npm@6.11`. You may update your npm via `npm install -g npm`. – k0pernikus Aug 28 '19 at 13:43
  • 1
    @MagicLAMP And `ci` is not a shorthand, is it its own command with distinct behavior, namely: it nukes node_modules, installs all dependencies from the package-lock.json, without ever mutating it. Read the linked page for further details: https://docs.npmjs.com/cli/ci – k0pernikus Aug 28 '19 at 13:49
  • 2
    Fwiw the `.gitignore` on the [_webpack_ Github](https://github.com/webpack/webpack/blob/master/.gitignore) does include `package-lock.json`... – JSStuball Jan 28 '20 at 19:16
  • 1
    @JSStuball webpack uses yarn instead of npm. It has a yarn.lock (equivalent to package-lock.json) in its repository. See also: https://github.com/webpack/webpack/pull/6930#issuecomment-377987981 – k0pernikus Jan 28 '20 at 19:49
  • 2
    @k0pernikus finally got npm ci working on my ansible deploy, and committed package-lock to the repository. This allows my front end developer to deploy things. Thanks. – MagicLAMP May 04 '20 at 03:07
  • don't use "npm ci" locally. It wastes network bandwidth..in my case it downloads chromium (150MB) every time – Jundl Feb 01 '21 at 16:32
  • @Jundl `npm ci` should still use the cache inside `~/.npm`. It only nukes the `node_modules`, but it should not always download everything. Is there anything special about your setup? – k0pernikus Feb 02 '21 at 01:10
  • hm, I develop a node module and in the using typescript project "npm install" is periodically executed. Chromium is a dependency of the module under development – Jundl Feb 02 '21 at 10:43
  • @Jundl Could you please open a new question in the form of "Why does npm ci not use cache for chromium dependency?" and reference minimal, complete and verifyable example of a package.json of yours? I would have a look then. You could link your question here. – k0pernikus Feb 02 '21 at 11:14