npm / cli

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

[BUG] `npm unpublish --force <package-name>@<version>` deletes all versions #7015

Closed trusktr closed 10 months ago

trusktr commented 11 months ago

Current Behavior

I ran

npm unpublish @lume/cli@0.10.1

and it would not work. The output told me I needed to add --force. I added --force,

npm unpublish --force @lume/cli@0.10.1

and this deleted the whole package, all the versions, despite having specified a version.

NPM support told me this can't be fixed. ๐Ÿ˜ ๐Ÿ˜ก๐Ÿ‘ฟ

Expected Behavior

Expected behavior is npm deletes only the specified version. Big ass WTF.

https://x.com/trusktr/status/1726910009580339544?s=20

The documentation clearly says:

To unpublish a single package version, run npm unpublish <package_name>@<version>.

If all the versions of a package can be unpublished, you can unpublish all versions at once by running npm unpublish <package_name> --force.

It does not state that --force ignores any specified version, which is mind blowing. ๐Ÿคฏ

I AM ANGRY!!!!!!!!!!!!!! ๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก

Is there an existing issue for this?

This issue exists in the latest npm version

Steps To Reproduce

Steps are above.

Environment

wraithgar commented 11 months ago

If npm is asking you to type --force whent it detects that you are unpublishing the only version of a package. Was that the only version of the package that existed when you typed this command?

The output telling you to use --force says:

Refusing to delete entire project.
Run with --force to do this.

npm is telling you it's trying to delete the whole project here.

wraithgar commented 11 months ago

There are a lot of safeguards in npm that should prevent this from happening. It will only send a total package unpublish if you specify just a package name or are unpublishing the last existing version of a package.

Can you double check your shell history to see exactly what you typed?

We can't reproduce this on our end w/ any combination of parameters.

hashtagchris commented 11 months ago

There's several versions of the package now, and I was able to install version 0.11.0. Some more questions to aid our investigation:

ljharb commented 11 months ago

Unpublished package versions can't ever be restored or republished, generally, so the second bullet point shouldn't be possible.

trusktr commented 10 months ago

Was that the only version of the package that existed when you typed this command?

No, there were multiple versions of the package published, f.e. 0.8.0, 0.9.0, and 0.10.0, among others.

The output telling you to use --force says:

Refusing to delete entire project.
Run with --force to do this.

npm is telling you it's trying to delete the whole project here.

Indeed that was fishy. I was trying to delete a specific version, and it was giving that message.

It will only send a total package unpublish if you specify just a package name or are unpublishing the last existing version of a package.

Can you double check your shell history to see exactly what you typed?

The commands in the OP were what I ran, but with single quotes around the package@version, while the last three published versions at the time were 0.10.0, 0.10.1, and 0.10.2.

$ history | grep "npm unpublish"
 4401  npm unpublish @lume/cli@0.10.1
 4402  npm unpublish --force --dry-run '@lume/cli@0.10.1'
 4404  npm unpublish '@lume/cli@0.10.1'
 4405  npm unpublish --force '@lume/cli@0.10.1'
 4408  npm unpublish --force '@lume/cli@0.10.2'

There you can see my commands. The first one gave me the --force message. I tried it with a dry run next, and with single quotes just in case. Then I tried again without --force but with single quotes and it told me I needed to use --force. Then the second-to-last command deleted all versions. I tried the last command before I realized all versions were deleted.

There's several versions of the package now, and I was able to install version 0.11.0. Some more questions to aid our investigation:

I of course went and published a new patch version for each minor to restore the delete package because support told me it could not be restored. I published a new patch for each 0.X version that got deleted. For example you can see version 0.9.4 which is a re-publish of 0.9.3 that got deleted, or for example 0.7.6 is a re-publish of 0.7.5 that was deleted. 0.10.3 is a re-publish of 0.10.0 (I was trying to delete 0.10.1 and 0.10.2, and I re-published 0.10.2 as 0.11.0).

What evidence did you see that the entire package was deleted?

It became a 404 page on npmjs.com, until I re-published the new patches for each minor.

  • Did you execute npm unpublish --force @lume/cli@0.10.1 with or without single quotes around @lume/cli@0.10.1? What shell did you use?

Sorry, the OP was not entirely accurate: I used single quotes on the command that did the deletion of all versions (as per above history output)

trusktr commented 10 months ago

I'm not angry anymore (and was never angry at anyone specifically), as I re-published the patches and went on my way. At worst, someone at a previous company might get an error and need to update a package.json version or a lock file.

Hope you're able to eventually reproduce this, because it really did happen!

trusktr commented 10 months ago

I'm able to consistently reproduce the following:

โฏ npm unpublish --dry-run '@lume/element@0.11.1'
npm WARN ignoring workspace config at /Users/trusktr/src/lume+lume/packages/element/.npmrc 
npm ERR! code EUSAGE
npm ERR! 
npm ERR! Refusing to delete entire project(s).
npm ERR! Run with --force to do this.
npm ERR! 
npm ERR! Remove a package from the registry
npm ERR! 
npm ERR! Usage:
npm ERR! npm unpublish [<package-spec>]
npm ERR! 
npm ERR! Options:
npm ERR! [--dry-run] [-f|--force]
npm ERR! [-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]]
npm ERR! [-ws|--workspaces]
npm ERR! 
npm ERR! Run "npm help unpublish" for more info

npm ERR! A complete log of this run can be found in: /Users/trusktr/.npm/_logs/2023-11-29T05_27_53_659Z-debug-0.log

where the latest published version is 0.11.4, and I have:

Node: v20.6.1 npm: 10.2.0

Here's a full reproduction on my end:

git clone git@github.com:lume/lume.git lume-npm-unpublish-test
cd lume-npm-unpublish-test
git checkout v0.3.0-alpha.34
git submodule update --init
cd packages/element/
npm unpublish --dry-run '@lume/element@0.11.1'

Maybe workspaces throws something off?

trusktr commented 10 months ago

Sometimes I can reproduce, sometimes I can't. Does it have to do with git submodules? For example I canceled (ctrl+c) out of the git submodule update --init half way through, then ran it again to make it complete, and when I went into packages/element/ I could no longer reproduce and would not get the --force error. Then I tried the reproduction steps over again and did not cancel git submodule update --init and now I get the error every time.

wraithgar commented 10 months ago

Not sure about the ctrl+c state but if I had to guess it would be a state where the workspace's package.json hadn't gotten written to disk.

This is definitely an issue w/ unpublishing workspaces and I'm surprised it went this long w/o any one seeing it. Your reproduction example was key to solving it, cheers.

wraithgar commented 10 months ago

The TLDR here is that the unpublish workspaces code was written before "automatically detect workspaces" was implemented. So when you're in that directory the code that is running was intended to be ran as npm unpublish -w foo which was conceptually equivalent to "cd foo; npm unpublish"

trusktr commented 10 months ago

Your reproduction example was key to solving it, cheers.

Yay! ๐Ÿ™ Happy holidays.