Skip to content
This repository has been archived by the owner on Aug 11, 2022. It is now read-only.

Can't use third-party modules in uninstall hook #11894

Open
archerrr opened this issue Mar 14, 2016 · 8 comments
Open

Can't use third-party modules in uninstall hook #11894

archerrr opened this issue Mar 14, 2016 · 8 comments

Comments

@archerrr
Copy link

Hey folks,

I can't find anything in issues or changelog. It worked with Node 0.12 (npm 2.14.9) but fails with 4 and 5 (npm 3.7.3).

In 4+ third-party modules are uninstalled before running uninstall hook making it impossible to require them in uninstall script.
Not being able to require modules makes it complicated to clean stuff on uninstall.

Is it a new feature or a bug or is there a new better way to do cleaning?

uninstall.js

var chalk = require('chalk')
console.log(chalk.green('done'))

package.json

{
  "uninstall": "node uninstall.js"
}
nvm use 0.12
npm link
npm rm
...
done
nvm use 5
npm link
npm rm
...
> node uninstall.js

module.js:338
    throw err;
          ^
Error: Cannot find module 'chalk'
...
@zkat zkat added the bug label Mar 16, 2016
@zkat
Copy link
Contributor

zkat commented Mar 16, 2016

It's little surprise that we had some lifecycle-related changes after npm@3 got launched, since a lot of that stuff was actually underspecified.

It's possible that preuninstall might help you here, but this part of the installer is a fairly involved bit of code to suss out. What you are seeing is not the intended behavior, and this is definitely a bug. Nice catch, and thanks for reporting it! 🎉

@iarna
Copy link
Contributor

iarna commented Mar 16, 2016

I tried this and I can't reproduce it in 3.7.3 or 3.8.1:

$ mkdir a
$ cat > a/package.json
{
  "name": "a",
  "version": "1.0.0",
  "scripts": {
    "uninstall": "node uninstall.js"
  },
  "dependencies": {
    "chalk": "*"
  }
}
^D
$ cat > a/uninstall.js
var chalk = require('chalk')
console.log(chalk.green('done'))
^D
$ mkdir node_modules
$ npm install a
/Users/rebecca/code/npmtest/uninstall
└─┬ a@1.0.0
  └─┬ chalk@1.1.1
    ├─┬ ansi-styles@2.2.0
    │ └── color-convert@1.0.0
    ├── escape-string-regexp@1.0.5
    ├─┬ has-ansi@2.0.0
    │ └── ansi-regex@2.0.0
    ├── strip-ansi@3.0.1
    └── supports-color@2.0.0
$ npm uninstall a

> a@1.0.0 uninstall /Users/rebecca/code/npmtest/uninstall/node_modules/a
> node uninstall.js

done
- a@1.0.0 node_modules/a
- ansi-regex@2.0.0 node_modules/ansi-regex
- ansi-styles@2.2.0 node_modules/ansi-styles
- chalk@1.1.1 node_modules/chalk
- color-convert@1.0.0 node_modules/color-convert
- escape-string-regexp@1.0.5 node_modules/escape-string-regexp
- has-ansi@2.0.0 node_modules/has-ansi
- strip-ansi@3.0.1 node_modules/strip-ansi
- supports-color@2.0.0 node_modules/supports-color

@archerrr
Copy link
Author

Thank you very much for the feedback.
That's weird using your code I'm able to reproduce (I've installed Node using nvm) and I'm on OS X El Capitan (10.11.3).

~ $ mkdir uninstall
~ $ cd uninstall/
~/uninstall $ npm -v
3.7.3
~/uninstall $ node -v
v5.8.0
~/uninstall $ mkdir a
~/uninstall $ cat > a/package.json
{
  "name": "a",
  "version": "1.0.0",
  "scripts": {
    "uninstall": "node uninstall.js"
  },
  "dependencies": {
    "chalk": "*"
  }
}
^C                               
~/uninstall $ cat > a/uninstall.js
var chalk = require('chalk')
console.log(chalk.green('done'))
^C
~/uninstall $ mkdir node_modules
~/uninstall $ npm install a
/Users/aaron/uninstall
└─┬ a@1.0.0 
  └─┬ chalk@1.1.1 
    ├─┬ ansi-styles@2.2.0 
    │ └── color-convert@1.0.0 
    ├── escape-string-regexp@1.0.5 
    ├─┬ has-ansi@2.0.0 
    │ └── ansi-regex@2.0.0 
    ├── strip-ansi@3.0.1 
    └── supports-color@2.0.0 

npm WARN enoent ENOENT: no such file or directory, open '/Users/aaron/uninstall/package.json'
npm WARN uninstall No description
npm WARN uninstall No repository field.
npm WARN uninstall No README data
npm WARN uninstall No license field.
~/uninstall $ npm uninstall a

> a@1.0.0 uninstall /Users/aaron/uninstall/node_modules/a
> node uninstall.js


module.js:341
    throw err;
    ^

Error: Cannot find module 'chalk'
    at Function.Module._resolveFilename (module.js:339:15)
    at Function.Module._load (module.js:290:25)
    at Module.require (module.js:367:17)
    at require (internal/module.js:16:19)
    at Object.<anonymous> (/Users/aaron/uninstall/node_modules/a/uninstall.js:1:75)
    at Module._compile (module.js:413:34)
    at Object.Module._extensions..js (module.js:422:10)
    at Module.load (module.js:357:32)
    at Function.Module._load (module.js:314:12)
    at Function.Module.runMain (module.js:447:10)
npm WARN lifecycle a@1.0.0~uninstall: continuing anyway a@1.0.0 uninstall: `node uninstall.js`
npm WARN lifecycle Exit status 1
- a@1.0.0 node_modules/a
- ansi-regex@2.0.0 node_modules/ansi-regex
- ansi-styles@2.2.0 node_modules/ansi-styles
- chalk@1.1.1 node_modules/chalk
- color-convert@1.0.0 node_modules/color-convert
- escape-string-regexp@1.0.5 node_modules/escape-string-regexp
- has-ansi@2.0.0 node_modules/has-ansi
- strip-ansi@3.0.1 node_modules/strip-ansi
- supports-color@2.0.0 node_modules/supports-color
npm WARN enoent ENOENT: no such file or directory, open '/Users/aaron/uninstall/package.json'
npm WARN uninstall No description
npm WARN uninstall No repository field.
npm WARN uninstall No README data
npm WARN uninstall No license field.
~/uninstall $ 

Do you want me to try something else?

I've also tried to read node_moduleswhen uninstalling:

~/uninstall $ npm install a
~/uninstall $ cat > node_modules/a/uninstall.js 
console.log(require('fs').readdirSync(__dirname + '/..'))
^C
~/uninstall $ npm uninstall a

> a@1.0.0 uninstall /Users/aaron/uninstall/node_modules/a
> node uninstall.js

[ '.staging', 'a' ]
- a@1.0.0 node_modules/a
- ansi-regex@2.0.0 node_modules/ansi-regex
- ansi-styles@2.2.0 node_modules/ansi-styles
- chalk@1.1.1 node_modules/chalk
- color-convert@1.0.0 node_modules/color-convert
- escape-string-regexp@1.0.5 node_modules/escape-string-regexp
- has-ansi@2.0.0 node_modules/has-ansi
- strip-ansi@3.0.1 node_modules/strip-ansi
- supports-color@2.0.0 node_modules/supports-color
npm WARN enoent ENOENT: no such file or directory, open '/Users/aaron/uninstall/package.json'
npm WARN uninstall No description
npm WARN uninstall No repository field.
npm WARN uninstall No README data
npm WARN uninstall No license field.

@NotMyself
Copy link

Here is a repository with a repro of this bug: https://github.com/NotMyself/ghost-azurestorage

Note that preuninstall fails because rimraf is removed before the script is called.

@alisianoi
Copy link

I have also run into this issue (uninstall.js requires a module that gets uninstalled right before that)

However, the minimum working example from @iarna did not trigger the bug for me. But in the real world the bug gets triggered. My versions:

  • npm -v is 3.10.3
  • node -v is v6.3.0
  • node/npm is setup via nvm
  • OS is ArchLinux

alisianoi pushed a commit to jzaefferer/commitplease that referenced this issue Aug 8, 2016
Make uninstall.js independent of 3rd party modules. There is an npm v3
bug that (sometimes) uninstalls 3rd party modules before running your
uninstall script which may rely on those modules.

Refs npm/npm#11894
alisianoi pushed a commit to jzaefferer/commitplease that referenced this issue Aug 8, 2016
Make uninstall.js independent of 3rd party modules. There is an npm v3
bug that (sometimes) uninstalls 3rd party modules before running your
uninstall script which may rely on those modules.

Refs npm/npm#11894
alisianoi pushed a commit to jzaefferer/commitplease that referenced this issue Aug 8, 2016
Make uninstall.js independent of 3rd party modules. There is an npm v3
bug that (sometimes) uninstalls 3rd party modules before running your
uninstall script which may rely on those modules.

Refs npm/npm#11894
@CedricReichenbach
Copy link

We're facing this problem too, seen on Windows and OS X, both through npm uninstall -g.

It seems to me there's a racing condition sometimes moving node_modules away before preuninstall hooks are run. I was able to sometimes reproduce this by adding the following snippet to the top of my preuninstall script:

console.log('preuninstall loaded')

// busy-wait for 20 seconds
var waitUntil = new Date(new Date().getTime() + 20000);
while (waitUntil > new Date()) {}

console.log('block released')

When preuninstall loaded was printed, mymodule/node_modules had sometimes been moved to .mymodule.MODULES. This seems happen in lib/install/remove.js inside removeDir, but I don't see right now where preuninstall is triggered and thus where the racing condition might be.

robertkowalski pushed a commit to robertkowalski/magnolia-cli that referenced this issue Mar 16, 2017
Due to a bug in npm[1], there is a racing condition where node_modules
sometimes gets removed (or moved away) before preuninstall scripts start.
As a consequence, 'module not found' errors break the uninstallation
workflow.

[1]: npm/npm#11894
@SamVerschueren
Copy link

Because I was affected by this, and apparently this is already a problem for more then 1 year, I created a repository that illustrates the problem.

https://github.com/SamVerschueren/npm-iss11894

  1. npm install -g npm-iss11894
  2. You can now see the files that are installed (ls is being executed in postinstall)
  3. npm uninstall -g npm-iss11894
  4. ls is being executed again as preuninstall but node_modules are missing

screen shot 2017-05-23 at 21 47 13

npm: v3.10.8
node: 6.9.1

@chrisui
Copy link

chrisui commented Jun 13, 2017

husky is itself an example of this. install it as a dep or try running it on ci with latest node/npm and you get "unable to find module". See typicode/husky#109 (comment)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

8 participants