Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

child-process: support any shells on Windows #21943

Closed
wants to merge 16 commits into from

Conversation

tkamenoko
Copy link
Contributor

@tkamenoko tkamenoko commented Jul 23, 2018

On Windows, child_process methods like exec supported only cmd.exe as the shell. This change enables these methods to accept any shells using -c switch like Unix shells.

Fixes: #21905

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • documentation is changed or added
  • commit message follows commit guidelines

@nodejs-github-bot nodejs-github-bot added the child_process Issues and PRs related to the child_process subsystem. label Jul 23, 2018
@addaleax addaleax added the windows Issues and PRs related to the Windows platform. label Jul 23, 2018
@@ -399,7 +400,7 @@ function normalizeSpawnArguments(file, args, options) {
if (Array.isArray(args)) {
args = args.slice(0);
} else if (args !== undefined &&
(args === null || typeof args !== 'object')) {
(args === null || typeof args !== 'object')) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you undo unrelated style changes? They make review harder, break git blame, and tend to induce merge conflicts.

test('cmd.exe');
test('C:\\WINDOWS\\system32\\cmd.exe');
test('powershell');
test('C:\\Program Files\\Git\\bin\\bash.exe');
Copy link
Member

@targos targos Jul 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it a build requirement to have git bash installed on Windows?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, git bash is one of prerequisites.

Basic Unix tools required for some tests, Git for Windows includes Git Bash and tools which can be included in the global PATH.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bash for windows is not a build requirement, but ATM it's a requirement for running the test suite.

node/BUILDING.md

Lines 265 to 267 in 5384570

* Basic Unix tools required for some tests,
[Git for Windows](http://git-scm.com/download/win) includes Git Bash
and tools which can be included in the global `PATH`.

However it is not guaranteed to be at C:\\Program Files\\Git. This could be done by running where bash, and also skipping if it not found.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not be assuming any paths, e.g. the SystemDrive might not be C:.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I had a misunderstanding.

Well, can I use exec in the exec test? If not, is there a workaround?

// Replace the test L23 to the following.
cp.exec('where bash', (error, stdout) => {
  if (error) {
    return;
  };
  test(stdout.trim());
});

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, or execSync.

@targos
Copy link
Member

targos commented Jul 24, 2018

test('cmd.exe');
test('C:\\WINDOWS\\system32\\cmd.exe');
test('powershell');
test('C:\\Program Files\\Git\\bin\\bash.exe');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bash for windows is not a build requirement, but ATM it's a requirement for running the test suite.

node/BUILDING.md

Lines 265 to 267 in 5384570

* Basic Unix tools required for some tests,
[Git for Windows](http://git-scm.com/download/win) includes Git Bash
and tools which can be included in the global `PATH`.

However it is not guaranteed to be at C:\\Program Files\\Git. This could be done by running where bash, and also skipping if it not found.

@tkamenoko
Copy link
Contributor Author

const shellFlags = platform === 'win32' ? ['/d', '/s', '/c'] : ['-c'];
const outputCmd = platform === 'win32' ? `"${cmd}"` : cmd;
const windowsVerbatim = platform === 'win32' ? true : undefined;

This test failed because it expects to use options like /d /s /c for any shells on Windows, but this PR changes this behavior. Is it OK to fix the test?

@gireeshpunathil
Copy link
Member

yes, absolutely!

@tkamenoko
Copy link
Contributor Author

Why did the test fail? I can't reproduce the error on my local repository.

not ok 17 async-hooks/test-fseventwrap

duration_ms: 0.176
severity: fail
exitcode: 1
stack: |-
internal/fs/watchers.js:167
throw error;
^

Error: ENOSPC: no space left on device, watch '/home/travis/build/nodejs/node/test/async-hooks/test-fseventwrap.js'
    at FSWatcher.start (internal/fs/watchers.js:161:26)
    at Object.watch (fs.js:1235:11)
    at Object.<anonymous> (/home/travis/build/nodejs/node/test/async-hooks/test-fseventwrap.js:16:20)
    at Module._compile (internal/modules/cjs/loader.js:689:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
    at Module.load (internal/modules/cjs/loader.js:599:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
    at Function.Module._load (internal/modules/cjs/loader.js:530:3)
    at Function.Module.runMain (internal/modules/cjs/loader.js:742:12)
    at startup (internal/bootstrap/node.js:266:19)

@targos
Copy link
Member

targos commented Jul 27, 2018

@tkamenoko Don't worry about this one. It happens often on Travis

@tkamenoko
Copy link
Contributor Author

@targos Thanks.
It seems that the other js tests than test-fseventwrap passed. What should I do?

@gireeshpunathil
Copy link
Member

started one more CI , to be certain.
CI: https://ci.nodejs.org/job/node-test-pull-request/16030/

@richardlau
Copy link
Member

Does any of the documentation need updating?

@vsemozhetbyt
Copy link
Contributor

Maybe this fragment?

https://github.com/nodejs/node/blame/449f73b7ffd60e7dd140b360e4b53dc2428f4aef/doc/api/child_process.md#L1375-L1376

@tkamenoko
Copy link
Contributor Author

Are http2 tests flaky?
https://ci.nodejs.org/job/node-test-binary-windows/18779/COMPILED_BY=vs2017,RUNNER=win2008r2-vs2017,RUN_SUBSET=1/tapResults/

parallel/test-http2-pipe failed on win2008r2. It passed on my local (win10 64bit), but parallel/test-http2-client-upload failed instead.

They both got the same error:

ExperimentalWarning: The http2 module is an experimental API.

Copy link
Member

@joaocgreis joaocgreis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tkamenoko thanks for working on this! I left a few comments that I believe should be addressed, but other than that this is looking good.

I noticed you are adding commits with descriptions for your changes. Usually when we land PRs we squash into only one commit (or more in some cases). In this case, we can keep the title and description of the first commit, so I want to make sure you're OK with that. This can happen only when landing, having multiple commits here in the PR is great if we ever have to come back and understand where something came from.

This PR changes the behavior described in https://github.com/nodejs/node/blob/a4ce449bb7b0e2f9edba75baf2a2f652a6750454/doc/api/child_process.md#shell-requirements . Since child_process is marked as stable, this will have to be semver major, and that section has to be adjusted to reflect these changes. Let me know if you'd like me to suggest some concrete text.

// '/d /s /c' is used only for cmd.exe.
if (file.endsWith('cmd.exe') || file.endsWith('cmd')) {
args = ['/d', '/s', '/c', `"${command}"`];
options.windowsVerbatimArguments = true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

windowsVerbatimArguments should be set out of this if block, to be active for any shell (this is mentioned in the documentation for spawn)

args = ['/d', '/s', '/c', `"${command}"`];
options.windowsVerbatimArguments = true;
// '/d /s /c' is used only for cmd.exe.
if (file.endsWith('cmd.exe') || file.endsWith('cmd')) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

String comparison should be case insensitive

if (error) {
return;
}
test(stdout.trim());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The output of where can have more than one line (in my machine I have bash from git and WSL). There is an example of using where here, but it can probably be simplified for this case.


test('cmd');
test('cmd.exe');
test('powershell');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also include a more complex PowerShell test (with quotes and pipes)? Something like

cp.exec(`Get-ChildItem "${__dirname}" | Select-Object -Property Name`,
        { shell: 'PowerShell' }, (error, stdout, stderror) => {
  assert.ok(!error && !stderror);
  assert.ok(stdout.includes('test-child-process-exec-any-shells-windows.js'));
});

@joaocgreis joaocgreis added semver-major PRs that contain breaking changes and should be released in the next major version. dont-land-on-v6.x labels Jul 31, 2018
@joaocgreis
Copy link
Member

@nodejs/platform-windows I like this as is, but when this lands people will start depending on this. So, now is a probably good time to discuss: do we want to have an explicit PowerShell case like the one this creates for CMD? Like CMD uses /D (#8063), PowerShell could have -NoProfile. And perhaps it would be useful to have -ExecutionPolicy Unrestricted to easily execute scripts.

@tkamenoko
Copy link
Contributor Author

@joaocgreis I have 2 questions just to make sure:

1: I no longer need to write long descriptions when adding commits, right? Or, should I write descriptions?

2:

windowsVerbatimArguments should be set out of this if block, to be active for any shell (this is mentioned in the documentation for spawn)

Do you mean that the code should be like this?

      if (file.endsWith('cmd.exe') || file.endsWith('cmd')) {// fix later
        args = ['/d', '/s', '/c', `"${command}"`];
      } else {
        args = ['-c', command];
      }
      options.windowsVerbatimArguments = true;

Thanks.

@joaocgreis
Copy link
Member

@tkamenoko

1: no need to write descriptions, just needs to be clear to the person landing this what to do with the commit. I usually write something like fixup: small description. There is no established procedure around this, so as long as it's clear it's good.

2: yes, that looks correct to me.

@bzoz
Copy link
Contributor

bzoz commented Aug 1, 2018

@tkamenoko can you make this more general? Not all shells will use -c as command, IMHO we should pass those extra shell args as an additional spawn option.

@tkamenoko
Copy link
Contributor Author

@bzoz Sorry, I can't decide by myself.

This PR changes Windows shell requirements to that like Unix, except for cmd.exe.
https://github.com/nodejs/node/blob/a4ce449bb7b0e2f9edba75baf2a2f652a6750454/doc/api/child_process.md#shell-requirements

Shell Requirements
The shell should understand the -c switch on UNIX or /d /s /c on Windows. On Windows, command line parsing should be compatible with 'cmd.exe'.

Your suggestion is interesting, but I don't know how to implement it without large fix. We need to discuss whether to accept shells that do not use -c. If you know shells like that, it will be helpful to get agreement.

@joaocgreis
Copy link
Member

@bzoz I think of exec as a way to run a line as if typed on the specified shell, it should know what arguments to use for each shell. Having an option to specify arguments would be ok, but I think it can be done in another PR if anyone is interested. Or do you think this PR should block on that? Note that quoting of the command depends on the arguments.

@tkamenoko your update looks good, but I'm having issues running the test locally. Please let me investigate.

The documentation update is still needed in https://github.com/nodejs/node/blob/a4ce449bb7b0e2f9edba75baf2a2f652a6750454/doc/api/child_process.md#shell-requirements . I suggest something like this:

The shell should understand the `-c` switch. If the shell is `'cmd.exe'`, it
should understand the `/d /s /c` switches and command line parsing should be
compatible.

@bzoz
Copy link
Contributor

bzoz commented Aug 6, 2018

The shell argument thingy can be done in another PR, this PR can land as it is.

@joaocgreis
Copy link
Member

@joaocgreis
Copy link
Member

Test failure is related: the full listing of our parallel folder is too big for the exec buffer.

tkamenoko and others added 15 commits September 1, 2018 23:32
On Windows, normalizeSpawnArguments set "/d /s /c" for any shells.
It cause exec and other methods are limited to cmd.exe as a shell.

Powershell and git-bash are often used instead of cmd.exe,
and they can recieve "-c" switch like unix shells.
So normalizeSpawnArguments is changed to set "/d /s /c" for cmd.exe,
and "-c" for others.

Fixes: nodejs#21905
Modify indent, and replace doublequote to singlequote.
This test ensure that child_process.exec can work with any shells.
Giving a shell name if $path is defined, or full path is given.

Testing with cmd, powershell, and git-bash, but
the test fails when testing with git-bash. Git-bash is usually placed
at 'C:\Program Files\Git\bin\', and it has a space that cause
the failure.
When giving 'echo foo bar' to powershell,
 the result contains a new line like:
foo
bar

This commit enables to test with poweshell correctly.
Before this commit, if given path contained spaces,
like 'Program Files', exec failed to find a shell.

'options.windowsVerbatimArguments' is used for cmd.exe
only, and spaces are escaped correctly.
Auto-formatting cause unnecessary indentations.
These style changes are removed.
test-child-process-any-shell uses `where` before testing with bash
to remove hard coding path.
This test expected to use cmd.exe options even if
the shell was powershell. This commit fixes to check correct options
for shells other than cmd.exe.
@joaocgreis
Copy link
Member

@tkamenoko I pushed a commit to address the test failure. Feel free to change it or remove it if you don't agree with it.

Rebased and new CI: https://ci.nodejs.org/job/node-test-pull-request/16929/

@joaocgreis
Copy link
Member

Landed in af883e1

Thanks for your contribution @tkamenoko !

@joaocgreis joaocgreis closed this Sep 5, 2018
joaocgreis pushed a commit that referenced this pull request Sep 5, 2018
On Windows, normalizeSpawnArguments set "/d /s /c" for any shells.
It cause exec and other methods are limited to cmd.exe as a shell.

Powershell and git-bash are often used instead of cmd.exe,
and they can recieve "-c" switch like unix shells.
So normalizeSpawnArguments is changed to set "/d /s /c" for cmd.exe,
and "-c" for others.

Fixes: #21905
PR-URL: #21943
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: João Reis <reis@janeasystems.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Refael Ackermann <refack@gmail.com>
@ackvf
Copy link

ackvf commented Oct 8, 2018

In which version of node was this released?

@richardlau
Copy link
Member

In which version of node was this released?

@ackvf It hasn't. This is semver-major so will be in Node.js 11.

@richardlau richardlau mentioned this pull request Apr 6, 2020
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
child_process Issues and PRs related to the child_process subsystem. semver-major PRs that contain breaking changes and should be released in the next major version. windows Issues and PRs related to the Windows platform.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Feature request:child_process.exec for powershell