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

npm run-script doesn't work on windows when the script contains multiple commands #4040

Closed
balupton opened this issue Oct 25, 2013 · 19 comments
Labels

Comments

@balupton
Copy link

If I have:

{
  "scripts": {
    "test": "node ./out/test/everything-test.js",
    "prepublish": "./node_modules/.bin/projectz compile; npm test"
  }
}

And I run npm run-script prepublish on windows, no script will execute.

C:\Users\Benjamin\Documents\GitHub\ambi [dev-cakefile-replace +0 ~6 -0]> npm run-script prepublish

> ambi@2.1.4 prepublish C:\Users\Benjamin\Documents\GitHub\ambi
> node ./node_modules/projectz/bin/projectz compile; npm test

If I run the scripts manually, it works:

C:\Users\Benjamin\Documents\GitHub\ambi [dev-cakefile-replace +0 ~6 -0]> node ./node_modules/projectz/bin/projectz compile; npm test
info: Initialising project
info: Initialised project
info: Loading changes
info: Loaded 2 contributors from bevry/ambi repository
info: Loaded changes
info: Writing changes...
info: Writing package file: C:\Users\Benjamin\Documents\GitHub\ambi\package.json
info: Writing package file: C:\Users\Benjamin\Documents\GitHub\ambi\bower.json
info: Writing package file: C:\Users\Benjamin\Documents\GitHub\ambi\component.json
info: Skipping package file: jquery
info: Writing readme file: readme
info: Writing readme file: history
info: Skipping readme file: contributing
info: Skipping readme file: backers
info: Writing readme file: license
info: Wrote changes
info: Saved changes

> ambi@2.1.4 test C:\Users\Benjamin\Documents\GitHub\ambi
> node ./out/test/everything-test.js

ambi
ambi > should handle result on successful synchronous functions
ambi > should handle result on successful synchronous functions OK
ambi > should handle result on successful asynchronous functions
ambi > should handle result on successful asynchronous functions OK
ambi > should handle returned errors on unsuccessful synchronous functions
ambi > should handle returned errors on unsuccessful synchronous functions OK
ambi > should handle callbacked errors on unsuccessful asynchronous functions
ambi > should handle callbacked errors on unsuccessful asynchronous functions OK
ambi > should ignore returned errors on successfull asynchronous functions
ambi > should ignore returned errors on successfull asynchronous functions OK
ambi > should ignore returned errors on unsuccessful asynchronous functions
ambi > should ignore returned errors on unsuccessful asynchronous functions OK
ambi > should NOT handle thrown errors on unsuccessful synchronous functions
ambi > should NOT handle thrown errors on unsuccessful synchronous functions OK
ambi > should NOT handle thrown errors on unsuccessful asynchronous functions
ambi > should NOT handle thrown errors on unsuccessful asynchronous functions OK
ambi > should NOT handle asynchronous thrown errors on unsuccessful asynchronous functions
ambi > should NOT handle asynchronous thrown errors on unsuccessful asynchronous functions OK
ambi OK

9/9 tests ran successfully, everything passed
C:\Users\Benjamin\Documents\GitHub\ambi [dev-cakefile-replace +0 ~6 -0]>
@balupton
Copy link
Author

Hrmmm, running the joined commands via child_process.exec doesn't work either:

C:\Users\Benjamin\Documents\GitHub\ambi [dev-cakefile-replace +0 ~6 -0]> node -e "require('child_process').exec('node ./node_modules/projectz/bin/projectz compile; npm test', {env:process.env, cwd:process.cwd()}, console.log)"
null '' ''

@bear
Copy link

bear commented Oct 25, 2013

npm script runs are handled on linux/osx by calling sh so I would imagine on windows it is done by calling cmd.exe (or whatever your environment has defined) and as such I can't imagine you could expect ; to work across all OS's

even on linux I have trouble getting complex command lines to survive the journey thru nodejs and npm's script runs - so I just got used to creating wrapper bash scripts and letting the shebang line tell sh that it needs to spawn bash

i'm wondering if you can do something similar - create a foo.sh script and see if powershell's linux-like shell handling lets you get your commands run

@isaacs
Copy link
Contributor

isaacs commented Oct 27, 2013

Commands are passed verbatim to either cmd or sh. Note that on OS X and some Linuxes, sh is actually bash with the (misnamed!) POSIXLY_CORRECT flag turned on.

Keep your command lines simple. A bash script won't work on Windows, for obvious reasons. Write a node program, or better yet, just don't use an install command. Presumably your prepublish script could just be written as a Node program, and run npm test as a spawn at the end.

Also, never ever write ./node_modules/.bin/ in your scripts. Seriously, that should just throw. What if the dep is somewhere else, installed globally, or as a parent's dep? npm puts ./node_modules/.bin in the PATH environ for a reason. Do not tightly couple to non-portable platform-specific paths! </rant>

@isaacs isaacs closed this as completed Oct 27, 2013
@rlidwka
Copy link
Contributor

rlidwka commented Oct 28, 2013

What if the dep is somewhere else, installed globally, or as a parent's dep?

  1. As far as I know, if a dependency is installed globally or elsewhere, npm install won't find it and would install it locally anyway. So this command line would work.
  2. As far as I know, npm run-script doesn't handle parent's dep case correctly as well, so it's the same deal in this case.

So the only case when ./node_modules/.bin/ is actually worse than npm run-script is when a dependency is installed both as a parent and globally. Hmm, I suppose you're right though.

@plato-cambrian
Copy link

I want to both run bower install and update my webdriver during postinstall; with the same package.json usable on both windows and linux.

Is there way that will let both these scripts run without making a new file? Here's what I am doing now:
"postinstall": "node node_modules/bower/bin/bower install; node node_modules/protractor/bin/webdriver-manager update"

The relative paths are because I don't want to assume bower or protractor are installed globally.

@maranomynet
Copy link

@balupton, this should work:

"prepublish": "./node_modules/.bin/projectz compile && npm test"

The only 'catch' is that the second command (npm test) won't run if the first one fails.

@othiym23
Copy link
Contributor

@maranomynet the ./node_modules/.bin/ part of that path is redundant. As @isaacs says, npm run-script takes care of putting node_modules/.bin on the PATH for you.

@maranomynet
Copy link

oh, I assumed the semi-colon was part of his problem, because on Windows command-line semicolon delimits parameters, instead of operations.

I stumbled upon this post as I was dealing with two-step package.json scripts not running on Windows, because of this semi-colon issue.

As soon as I changed the semi-colon to & (windows-only replacement for ;) or && (conditional) everything started running smoothly. And && works cross-platform.

(http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/ntcmds_shelloverview.mspx?mfr=true)

Jeff-Lewis added a commit to Jeff-Lewis/blanket that referenced this issue Dec 1, 2015
- When running npm scripts, npm puts ./node_modules/.bin in the path so referencing hard coded ./node_modules/mocha/bin/mocha is not recommended nor is it cross platform. See npm/npm#4040 (comment)
- Updated mocha to 2.3.4
cjblomqvist added a commit to cjblomqvist/generator-derby that referenced this issue Jan 5, 2016
@fredericrous
Copy link

&& do not work on Powershell
&& is not cross platform.

@othiym23
Copy link
Contributor

npm does not run package scripts using PowerShell on Windows, @zougi. cmd.exe, which npm does use, doesn't have the exact same semantics for && (it executes the second command regardless of the success of the first), but it does work to string together multiple commands on a line.

@fredericrous
Copy link

Hi @othiym23. I did a re-test, you are right. I thought npm uses the shell from where it is called. Thanks for the clarification.

miketmoore added a commit to miketmoore/baltimore-vacant-buildings that referenced this issue May 18, 2016
See issue npm/npm#4040, comment by @othiym23
npm-run-script adds ./node_modules/.bin on the PATH
miketmoore added a commit to miketmoore/baltimore-vacant-buildings that referenced this issue May 27, 2016
See issue npm/npm#4040, comment by @othiym23
npm-run-script adds ./node_modules/.bin on the PATH
cheshire137 added a commit to cheshire137/gh-notifications-snoozer that referenced this issue Oct 16, 2016
@usergenic
Copy link

FWIW I just do this in my npm scripts to get them to work on windows AND linux/mac:

"something": "sh -c \"whatever | this && works\""

@Noxxys
Copy link

Noxxys commented Apr 5, 2017

sh is not recognized on Windows

@usergenic
Copy link

@Noxxys, which version of windows? I was only using a Windows 10 server to test.

@Noxxys
Copy link

Noxxys commented Apr 10, 2017

I'm using Windows 10, but it will be the same on Windows 7 and 8.1. sh is a Unix program, not installed on Windows unless you use a special shell.

@kurtmilam
Copy link

@usergenic: Thanks for the tip.
@Noxxys: I'm using this console emulator on Windows 10. 'sh' is recognized if I call it from a mintty or bash shell in the emulator. Just FYI for anyone looking for a reasonably easy solution to this issue.

@balupton
Copy link
Author

balupton commented Apr 16, 2017

wouldn't https://msdn.microsoft.com/en-us/commandline/wsl/install_guide solve this without having to change anything in our packages?

for an update from my side, I've just gone and used npm scripts for everything https://github.com/bevry/base/blob/7f88b7dd5994d0637595b39f92bb168bc82695b9/package.json#L85-L105 with bash conventions - and now that every OS that devs use has bash, I'm happy to just say to our devs use bash when running the npm scripts - I haven't yet tried this on windows, but I'd imagine it would work - it works on non-standard login shells like fish on mac and linux (fish doesn't use && but instead uses ; and), so it is good that the commands still seem executed by bash as Isaac mentioned earlier)


If it doesn't work on windows due to:

Commands are passed verbatim to either cmd or sh. Note that on OS X and some Linuxes, sh is actually bash with the (misnamed!) POSIXLY_CORRECT flag turned on.

Then it would be good if npm preferred bash or sh over cmd - by checking if bash or sh exists, and if it doesn't, then use cmd

@rainabba
Copy link

rainabba commented May 4, 2017

I'm dealing with the same issue. "&&" is what I'd normally use on windows, but it's not supported on POSIX. With Windows 10 1703 and WSL, I can use the sh trick that @usergenic mentioned, but my Windows Servers aren't running WSL so I'm still looking for a solution.

Why not just allow scripts (in package.json) to be string OR Array and when an array, parse it and execute each?

IDragonfire added a commit to IDragonfire/devour that referenced this issue May 8, 2017
IDragonfire added a commit to IDragonfire/devour that referenced this issue May 8, 2017
@brownieboy
Copy link

brownieboy commented Apr 27, 2018

Essentially, this is boolean logic. So you can use that boolean logic to force the second script to run, even if the first script fails, and yes it works on Windows (where the semi-colon syntax doesn't work). Here's what I originally had in the scripts property of my package.json:

"scripts": {
    testRun: "node ./runtest.js && node ./runreports.js",

In this case, runreports.js would only run if runjest.js completed succesfully - i.e. didn't crash with exit code="1" (or whatever). That's because it's a logical AND. So I changed it to this to a boolean OR, like so:

"scripts": {
    testRun "node ./runtest.js || node ./runreports.js",

Now I had the opposite problem: runreports.js only ran if runtest.js did crash!

The way to force runreports.js to always run is to ensure that the preceding condition always results in a boolean true. In boolean logic something OR true always results in true, i.e:

true || true = true
true || false = true
false || true = true

Applying that logic to my package.json script:

"scripts": {
    "(node ./runtest.js || true) && node ./runreports.js",

By wrapping he first script call with in some brackets and the ORing it with a true, the result of that bracketed section must always be true, no matter what the runtest.js script does. So runreports.js will always run.

It works for me on Windows anyway.

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

No branches or pull requests