npm / cli

the package manager for JavaScript
https://docs.npmjs.com/cli/
Other
8.25k stars 3.01k forks source link

[BUG] npm update does not save new versions in package.json #2704

Closed saltire closed 2 years ago

saltire commented 3 years ago

Current Behavior:

When running npm update, packages are updated normally, package-lock.json is updated, but package.json is not. (npm install [package]@[version] does update package.json as expected.)

Expected Behavior:

The package.json file should be updated with the newly installed versions, as specified in the npm-update docs.

Steps To Reproduce:

Environment:

OS: Mac OS Big Sur Node: 14.15.5 NPM: 7.5.4

mjlehrke commented 3 years ago

I experienced this bug on Windows, NPM version 6.14.11. npm update failed to update package.json but a targeted install worked.

Interestingly, it seemed my global packages were also messed up around the same time this started. For instance, npm list -g --depth=0 would show the global packages but some of them would have messed up versions such as snyk@0. npm outdated would insist the package was outdated even after updating. Re-installing the packages or npm didn't fix.

I was able to fix this by completely uninstalling node, npm, Windows build tools, and chocolatey. I deleted all the related folders in my user folder, programs folder, %appdata%, %localappdata%, and %programdata%. A simple node/npm update wouldn't fix it, so something was messed up in one of those folders. After re-install of node/npm, package.json was updating as expected and the global packages issue was resolved.

CageFox commented 3 years ago

Same bug on windows 10 node v12.16.0 After npm upgrade versions in package.json not automatically updated

darcyclarke commented 3 years ago

@saltire sorry for the confusion, we need to update the docs to clarify that npm update will install & update the package-lock.json but not modify the spec defined in package.json; As you noted, you can still update that by running npm install <pkg>@<version> - this was a breaking change from v6, as that previously would modify package.json

CageFox commented 3 years ago

@saltire sorry for the confusion, we need to update the docs to clarify that npm update will install & update the package-lock.json but not modify the spec defined in package.json; As you noted, you can still update that by running npm install <pkg>@<version> - this was a breaking change from v6, as that previously would modify package.json

And what command can I use to update all packages, modifying specs in package.json?

jlchereau commented 3 years ago

@saltire sorry for the confusion, we need to update the docs to clarify that npm update will install & update the package-lock.json but not modify the spec defined in package.json; As you noted, you can still update that by running npm install <pkg>@<version> - this was a breaking change from v6, as that previously would modify package.json

There should be a better way than running npm install <pkg>@<version> on each package to update package.json. Maybe it is also time to introduce wildcards (or regular expression as in ncu), especially with scopes as in npm update @babel/*@7

mlippert commented 3 years ago

Since what I want to do is update the versions listed in package.json to the latest "Wanted" version and not the "Latest" version (as shown by npm outdated), this becomes a laborious process and for now I think I'm probably better off staying on npm v6 or downgrading to npm v6 (if using node v14).

I have my package versions set to "want" the latest non-breaking changes if semver is respected. I use the version in the package.json to help me know the version I actually last got and tested locally. When a non-breaking change actually breaks something, I update package.json to lock the previous version, report the issue and track the package for a fix.

I'm also wondering what the rationale for the breaking change was. There was already an option --no-save if you didn't want your package.json modified. Now there is no way to get the v6 behavior, other than one by one installing the latest specific wanted version of each package.

ym-project commented 3 years ago

I was extremely surprised when npm up command didn't update package.json file. I really hoped that it was a cli bug but @darcyclarke upset me :(

srknzl commented 3 years ago

@saltire sorry for the confusion, we need to update the docs to clarify that npm update will install & update the package-lock.json but not modify the spec defined in package.json; As you noted, you can still update that by running npm install <pkg>@<version> - this was a breaking change from v6, as that previously would modify package.json

Still not sure how to update all packages and change package.json

ym-project commented 3 years ago

Several people recommended me npm package for dependencies updates. Maybe it will be useful for somebody.

srknzl commented 3 years ago

Using a package to update packages is so weird. This is not the way it should be. By the way is there a way for the package to do "exactly" what npm update was doing before?

ym-project commented 3 years ago

Using a package to update packages is so weird. This is not the way it should be. By the way is there a way for the package to do "exactly" what npm update was doing before?

You can write your own script something like this scripts/up.js

const {execSync} = require('child_process')
const packageName = process.argv[2]

const outdatedInfo = JSON.parse(execSync(`npm outdated -l --json ${packageName}`))

const packageCurrentVersion = outdatedInfo[packageName].current
const packageWantedVersion = outdatedInfo[packageName].wanted
const packageType = outdatedInfo[packageName].type
const isDev = packageType === 'devDependencies'

const updateInfo = execSync(`npm i ${isDev ? '-D' : ''} ${packageName}@${packageWantedVersion}`)

console.log(updateInfo.toString())

And then use node ./scripts/up webpack

I know it's not a good solution but why not?

ljharb commented 3 years ago

@srknzl npm is a package. It's really not weird.

srknzl commented 3 years ago

I mean why other 3rd party package if DeFacto standard is npm package

xavierfoucrier commented 3 years ago

@saltire sorry for the confusion, we need to update the docs to clarify that npm update will install & update the package-lock.json but not modify the spec defined in package.json; As you noted, you can still update that by running npm install <pkg>@<version> - this was a breaking change from v6, as that previously would modify package.json

This, is a serious breaking change / issue when migrating to v7, and this is not documented anywhere at this time: I just noticed that during migration.

The important questions now are:

Thanks for taking the time to give a clear answer to the community :wink:

jarrodek commented 3 years ago

For now, I decided not to upgrade to npm >= 7. With this behavior, I lose track of what is happening with my project, which dependencies I am upgrading, and which still need to be upgraded (via the npm up command). I can't rely on checking changes to the lock file as this file is not meant to be read by humans. If I may suggest at least adding a CLI option to update the spec file when updating dependencies. This way we could have the previous behavior as an opt-in.

Regards.

yelworc commented 3 years ago

Stumbled upon this issue after upgrading npm, skimmed another issue thread and this one; at least two maintainers claim that this was an intentional breaking change, yet I can't find the rationale behind it (in fact, I wasn't even able to find any mention of it in the npm v7 changelogs at first glance).

I'm fine with adjusting my workflow, but at the moment I can't quite figure out how this new behavior of npm update is better than before (as others have pointed out, seeing incorrect versions package.json seems confusing at best to me).

arash-bizcover commented 3 years ago

@npm/cli-team please let us know, How the hell can we update all modules in package.json dependencies to their latest, from now on???

jlchereau commented 3 years ago

@npm/cli-team please let us know, How the hell can we update all modules in package.json dependencies to their latest, from now on???

For now I have not found any alternative to npm i -g npm-check-updates && ncu -u. They should really implement this in npm@7.

ljharb commented 3 years ago

There’s also,npx salita

arash-bizcover commented 3 years ago

NPM Version 7 feels like if some .NET lovers have sneaked into the npm development team and trying to ruin the JS development experience.

n1ngu commented 3 years ago

And what command can I use to update all packages, modifying specs in package.json?

@CageFox @arash-bizcover the fact is npm@6 never udpated package.json beyond the specs defined within itself. It might be controversial because it is a breaking change with npm@6, but there is little point in automatically evolving this file.

If you distributed an app, you should be distributing a shrinkwarp file. If you distributed a library, you'd only bump the specs in package.json when manually conducting a major upgrade.

See also https://github.com/npm/cli/issues/708#issuecomment-840725231 and https://github.com/npm/feedback/discussions/270

arash-bizcover commented 3 years ago

@CageFox @arash-bizcover the fact is npm@6 never updated package.json beyond the specs defined within itself. It might be controversial because it is a breaking change with npm@6, but there is little point in automatically evolving this file.

@n1ngu yes That specs defined within itself is what exactly we are using, and there is a strong point automatically update based on the package.json file.

We(as well as many others) have tons of dependencies for our applications which we are the owner of those dependencies as well. When we update some dependencies we want our applications CICD pipelines to run npm update on test environments and without manually changing each bit on each application build and test the latest dependencies when our tests and health checks pass CICD just simply runs npm i and then automatically commit back(PR) package.json to the repository without anyone touching anything and everything gets updated and we can also see what is the latest version of our first-class dependencies easily on package.json.

n1ngu commented 3 years ago

We(as well as many others) have tons of dependencies for our applications which we are the owner of those dependencies as well. When we update some dependencies we want our applications CICD pipelines to run npm update on test environments and without manually changing each bit on each application build and test the latest dependencies when our tests and health checks pass CICD just simply runs npm i and then automatically commit back(PR) package.json to the repository without anyone touching anything and everything gets updated and we can also see what is the latest version of our first-class dependencies easily on package.json.

@arash-bizcover Since you are using the word "application" I'll insist: for that workflow you'd need to lock the dependencies in a shrinkwrap file. Then again, the package.json specs need not to evolve.

If you meant "library", you should only commit back the package-lock.json file, run your tests to check nothing broke, and let your library end-users update any transient dependency (yours or not) at the pace they want.

imarotte commented 2 years ago

I've been waiting patiently for for months now for this seemingly obvious bug to be fixed only to now figure out it is as designed. When I look at package.json I expect to see the packages and versions I'm currently working with. Is that not normal? I just cannot understand the benefit of not at least offering the option of updating package.json through a flag. Especially since that is the legacy behavior.

CageFox commented 2 years ago

Our whole team is stuck on npm 6 due to this bug. Npm 7 is useless for us.

@n1ngu Why is this so hard just to return back the feature we need? What you explained for us is yours way to do the things, it can not be acceptable for all, many people do the same things in another way and this bug prevents us from using npm 7 at all

Allain55 commented 2 years ago

I totally agree with @imarotte and @CageFox. We are in the same situation.

What is the point of having the exact, but wrong package version in package.json then? That's misleading. Why not only keep the major version if that is what matters anymore? In a perfect world where patch releases sometimes don't introduce breaking changes it could be fine, but it's never going to be that. And it could be really hard to debug problems when you are supposed to have the same package version like others but in reality not (I've been there)

n1ngu commented 2 years ago

@CageFox I have no stake in this. I'm just glad how npm@7 works, like all the people that will never show up in this discussion.

I just wanted to share the idea that this is not a bug. Sure, some people will have a very special workflow that relied on the old behavior. But for the bulk of people complaining here, I think the new behavior isn't really a blocker.

But if it worries you this much, read the linked feedback discussion: maintainers are open to recover the old behavior via cli flags.

@Allain55 if a dependency does not abide by semver, you should freeze it instead of using a caret version spec, and this would never let npm update to update it anyway.

sla100 commented 2 years ago

Maybe the command npm outdated shouldn't update anything by itself, just print a report table and prepared commands like:

npm update package1@wantedVersion
npm update -D package2@wantedVersion

The maintainer could then quickly review and consciously implement the updates by copy-paste.

n1ngu commented 2 years ago

[...] The maintainer could then quickly review and consciously implement the updates [...]

For that workflow, npm-check-updates got you covered https://github.com/npm/cli/issues/2704#issuecomment-882393993

Bonus: it can update dependencies beyond the defined specs, which no version of npm would do.

matthewmayer commented 2 years ago

While its not quite the same as the old behavior of npm update in npm@6 what i normally want to do is upgrade all minor/patch versions and write to package.json, but never upgrade major versions (prefer to do those one-by-one and manually). So if you generally use versions like

"cors": "^2.8.5" (caret for minor versions) then npx npm-check-updates --target minor --upgrade; npm install will upgrade all packages to latest minor version

If you are more risk-averse and only want to upgrade patch versions and your package.json is

"cors": "~2.8.5" (tilde for patch versions) then npx npm-check-updates --target minor --upgrade; npm install will upgrade all packages to latest patch version

However this won't work if you have a mix of ^ and ~ versions

ljharb commented 2 years ago

Something that would work (an alternative and a potential algorithm analogue) is, for each dependency/dev dep foo, run npm install foo which will respect the semver range in package.json.

matthewmayer commented 2 years ago

How about this:

npm install `node --print 'const package = require("./package.json");Object.keys(package.dependencies).join(" ")'`

This grabs the list of dependencies, turns into a space seperated list and passes it to npm install

ljharb commented 2 years ago

Include dev deps in there and you’ve got something workable.

matthewmayer commented 2 years ago

hmm one problem with my solution above is that if you have a frozen version like

"millify": "3.5.2",

it will change it to

"millify": "^3.5.2",

which is probably NOT what you want

Here's a new version which only does npm install for the version specifiers which start with ^ or ~

npm install `node --print 'const package = require("./package.json");Object.keys(package.dependencies).filter(key=>{const char = package.dependencies[key][0]; return char=="^"||char=="~"}).join(" ")'`
ljharb commented 2 years ago

That’s definitely what you want; a lockfile pins versions, and they shouldn’t be pinned in package.json.

matthewmayer commented 2 years ago

er no, if i specify "millify": "3.5.2" (because say 3.5.3 has a bug) i do NOT want to upgrade that package automatically ever.

ljharb commented 2 years ago

Then you’d use =3.5.2 to make it explicit.

matthewmayer commented 2 years ago

i must admit ive been using npm for years and never heard of the = operator. Is there any functional difference between

"millify": "3.5.2" and "millify": "=3.5.2"

matthewmayer commented 2 years ago

its not documented on https://docs.npmjs.com/cli/v7/configuring-npm/package-json#dependencies as far as i can see...

Screenshot 2021-11-04 at 13 22 44
ljharb commented 2 years ago

and yet, it’s a range.

matthewmayer commented 2 years ago

Sorry I don't understand. How is "millify": "3.5.2" a range? Doesn't that mean 3.5.2 exactly?

ljharb commented 2 years ago

No, i mean “=3.5.2” is a range - one that contains exactly one version.

jginsburgn commented 2 years ago

Will this get addressed?

StefanNedelchev commented 2 years ago

I recently updated to npm 8.3.0 and I received the "pleasant" surprise just like all of you. Since it seems that nothing has changed for more than an year, I'm really worried and I wonder if I should revert back to npm v6 or keep up with v8. If there was any hint that this behavior will be restored or at least introduced as an optional argument, I would stick to v8.

n1ngu commented 2 years ago

@jginsburgn @hardmaster92 Sorry but, as critical as this looks for you, could you please read the whole thread and the linked discussions before commenting that?

ruyadorno commented 2 years ago

The team has decided that fixing usage of npm update --save was the best way moving forwards 😊 it enables saving dependency ranges to package.json as expected. It's also worth noticing that you can also just set save=true in a .npmrc file in case you want that to be the default behavior..

npm@8.3.2 is out now with the fix 🎉

CageFox commented 2 years ago

It seems the issue resolved and now we can move to actual npm too

xavierfoucrier commented 2 years ago

@ruyadorno Great news! Big thanks for that :tada:

sla100 commented 2 years ago

.npmrc of 2022:

global-style=true
engine-strict=true
legacy-peer-deps=true
lockfile-version=3
save=true
ljharb commented 2 years ago

@sla100 legacy-peer-deps should be avoided; that's just papering over your invalid dependency graph, and since engines is purely advisory, engine-strict is going to break just as often as it helps you. I've never heard of global-style, and save=true is the default.