Skip to content

fix(powershell): use Invoke-Expression to pass args #8267

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

Open
wants to merge 42 commits into
base: latest
Choose a base branch
from

Conversation

alexsch01
Copy link

@alexsch01 alexsch01 commented May 1, 2025

Reopening of #7458

@mbtools
Copy link
Contributor

mbtools commented May 1, 2025

Hi Alex,

Any change here should go along with new tests that prove what's not working (before the change) but working after. This includes executing npm run xxx from the command line and npm run xxx in a script in package.json.

I have updated test/bin/windows-shims.js to cover passing regular and named parameters to scripts.

windows-shims.txt

Please include this in the PR.

On my machine (pwsh 7.5.0, npm 10.9.2), all parameters are passed correctly with the current npm.ps1.

Do you have other tests that fail? Please add them.

@alexsch01
Copy link
Author

@mbtools thanks! I'll work on new tests for this PR later today

@alexsch01
Copy link
Author

alexsch01 commented May 2, 2025

@mbtools

On my machine (pwsh 7.5.0, npm 10.9.2), all parameters are passed correctly with the current npm.ps1.

npm test -- --param1 -p doesn't work in Windows PowerShell and PowerShell 7.5.1 (pwsh) using npm 10.9.2

  • it doesn't pass --param1 and -p
  • this PR fixes that

@alexsch01
Copy link
Author

This PR is ready for review

@alexsch01
Copy link
Author

alexsch01 commented May 3, 2025

Below "Output" is from running the windows-shims.js from upstream WITHOUT my PR

This should pass before adding my PR changes


@mbtools Ok I have an issue running the tests on my Windows machine

Without my PR, running node .\test\bin\windows-shims.js gives 2 failures

line number 240 and line number 253 from https://github.com/npm/cli/blob/latest/test/bin/windows-shims.js

  • commit 2d1d8d0 if this "latest" file changes

Output

PS C:\Users\FakeUser\Downloads\cli> node .\test\bin\windows-shims.js
TAP version 14

Subtest: shim contents

1..3
# Subtest: bash
    ok 1 - should be equivalent strictly
    ok 2 - all other changes are m->x
    1..2
ok 1 - bash # time=4.053ms

# Subtest: cmd
    ok 1 - should be equivalent strictly
    ok 2 - all other changes are m->x
    1..2
ok 2 - cmd # time=1.276ms

# Subtest: pwsh
    ok 1 - should be equivalent strictly
    ok 2 - all other changes are m->x
    1..2
ok 3 - pwsh # time=2.412ms

ok 1 - shim contents # time=12.605ms

Subtest: node-gyp

ok 1 - node-gyp contains env var
ok 2 - node-gyp contains path
ok 3 - node-gyp.cmd contains env var
ok 4 - node-gyp.cmd contains path
1..4

ok 2 - node-gyp # time=0.827ms

Subtest: run shims

1..6
# Subtest: cmd
    1..2
    not ok 1 - npm.cmd npm
      ---
      diff: |
        --- expected
        +++ actual
        @@ -1,4 +1,4 @@
         Object {
        -  "status": 0,
        -  "stdout": "[email protected] C:\\Users\\FakeUser\\Downloads\\cli",
        +  "status": 1,
        +  "stdout": "",
         }
      at:
        fileName: test\bin\windows-shims.js
        lineNumber: 240
        columnNumber: 7
        functionName: matchCmd
        isToplevel: true
      stack: |
        matchCmd (test/bin/windows-shims.js:240:7)
        Test.<anonymous> (test/bin/windows-shims.js:263:7)
        Test.<anonymous> (test/bin/windows-shims.js:253:7)
        Object.<anonymous> (test/bin/windows-shims.js:77:3)
      source: "    const result = spawnPath(cmd, [...args, isNpm ? 'help' :
        '--version'], opts)\r

        \r

        \    t.match(result, {\r

        ------^

        \      status: 0,\r

        \      signal: null,\n"
      ...

    not ok 2 - npx.cmd npx
      ---
      diff: |
        --- expected
        +++ actual
        @@ -1,4 +1,4 @@
         Object {
        -  "status": 0,
        -  "stdout": "11.3.0",
        +  "status": 1,
        +  "stdout": "",
         }
      at:
        fileName: test\bin\windows-shims.js
        lineNumber: 240
        columnNumber: 7
        functionName: matchCmd
        isToplevel: true
      stack: |
        matchCmd (test/bin/windows-shims.js:240:7)
        Test.<anonymous> (test/bin/windows-shims.js:264:7)
        Test.<anonymous> (test/bin/windows-shims.js:253:7)
        Object.<anonymous> (test/bin/windows-shims.js:77:3)
      source: "    const result = spawnPath(cmd, [...args, isNpm ? 'help' :
        '--version'], opts)\r

        \r

        \    t.match(result, {\r

        ------^

        \      status: 0,\r

        \      signal: null,\n"
      ...

not ok 1 - cmd # time=1434.598ms
  ---
  at:
    fileName: test\bin\windows-shims.js
    lineNumber: 253
    columnNumber: 7
    typeName: Test
  source: "\r

    \  for (const { cmd, skip, name, match } of shells) {\r

    \    t.test(name, t => {\r

    ------^

    \      if (skip.reason) {\r

    \        if (skip.fail) {\n"
  ...

# Subtest: pwsh
    ok 1 - pwsh - not installed # SKIP
    1..1
ok 2 - pwsh # time=0.61ms

# Subtest: git bash
    ok 1 - git bash - not installed # SKIP
    1..1
ok 3 - git bash # time=0.507ms

# Subtest: user git bash
    ok 1 - user git bash - not installed # SKIP
    1..1
ok 4 - user git bash # time=1.478ms

# Subtest: wsl bash
    ok 1 - wsl bash - not installed # SKIP
    1..1
ok 5 - wsl bash # time=1.499ms

# Subtest: cygwin bash
    ok 1 - cygwin bash - not installed # SKIP
    1..1
ok 6 - cygwin bash # time=1.304ms

not ok 3 - run shims # time=1907.767ms

at:
fileName: test\bin\windows-shims.js
lineNumber: 77
columnNumber: 3
typeName: Object
source: "})\r

\r

t.test('run shims', t => {\r

--^

\  const path = t.testdir({\r

\    ...SHIMS,\n"

...

1..3

{ total: 17, pass: 10, fail: 2, skip: 5 }

time=1953.236ms

PS C:\Users\FakeUser\Downloads\cli>

@mbtools
Copy link
Contributor

mbtools commented May 3, 2025

This is not the same js as in this PR:

Your Log: const result = spawnPath(cmd, [...args, isNpm ? 'help' : '--version'], opts)\r
PR: const result = spawnPath(cmd, [...args, ...params], opts)

@alexsch01
Copy link
Author

This is not the same js as in this PR:

Your Log: const result = spawnPath(cmd, [...args, isNpm ? 'help' : '--version'], opts)\r PR: const result = spawnPath(cmd, [...args, ...params], opts)

Right...I meant I'm getting 2 failing tests before adding my PR

@mbtools
Copy link
Contributor

mbtools commented May 3, 2025

That's with Windows PowerShell? I'm not sure if this ever was an option (or tested). Here it's about cmd, pwsh, and bash. If we can make it work for the older Windows PowerShell as well, why not?

@noseratio
Copy link

My 2c. If it's possible to make it work for both legacy (v5.0 and below) and modern (v6+) PowerShell with little efforts, that's great. Otherwise, I'd say specifying #Requires -Version 6.0 should be enough. For Windows people, it's been long overdue to move over to the modern portable PowerShell.

@alexsch01
Copy link
Author

I manually tested that echo 'test'; npm help a=1,b=2,c=3 now works with this PR (locally not the test) in both Windows PowerShell and pwsh

@alexsch01
Copy link
Author

the 2 failing tests I get on my machine do not appear in GitHub Actions CI so it must be my setup issue

@alexsch01
Copy link
Author

alexsch01 commented May 5, 2025

It depends on how you pass the parameters to spawn. Args is an array but the test passes just one string (not split by parameters). The reason was testing the part after --. Now there are other tests that might require a split first.

The way we call spawn here actually must match how npm run script does it. Otherwise, the test results will be misleading. It's a separate package. I can look tomorrow.

it's bizarre spawnSync with Windows PowerShell gives what I think the correct behavior, where as spawnSync with pwsh7 gives what I think the wrong behavior


For both Windows PowerShell and pwsh7, doing .\abc.ps1 first a=1,b=2,c=3 gives

first
a=1
b=2
c=3

@alexsch01 alexsch01 marked this pull request as draft May 5, 2025 13:32
@alexsch01
Copy link
Author

alexsch01 commented May 5, 2025

Converted to a draft.....

this will fail the scenario in Windows PowerShell

{
  bin: 'npm',
  params: ['test -- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'],
  expected: `aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`
},

@alexsch01 alexsch01 marked this pull request as ready for review May 5, 2025 15:44
@alexsch01
Copy link
Author

Modifed the code to try to replicate $MyInvocation.Statement on Windows PowerShell

@mbtools
Copy link
Contributor

mbtools commented May 5, 2025

I was afraid of this slight mess...

Here's how npm run script works. It uses @npmcli/promise-spawn which has this big issue. I have seen this before when porting pacote tests to Windows and left this comment.

And here we are again... Calling spawn on Windows is a very tricky thing. There are solution like cross-spawn but that's not an option here.

I know you're interested in getting Windows PowerShell to work. Can we get back to the primary goal to solve issues with passing named parameters to PowerShell7 ($args doesn't work)?

@alexsch01
Copy link
Author

OK @mbtools, I'm all done. Let me know what you think of it

@mbtools
Copy link
Contributor

mbtools commented May 5, 2025

This is good

We are closer than ever 👍

image

Maybe we can combine some test cases. I used this one:

{ bin: 'npm', params: ['test -- -a 1 --b 2 --c=3 "4" --d="5"'], expected: -a\n1\n--b\n2\n--c=3\n4\n--d=5 },

Not so good

The solution fails if there are spaces in the path to node or npm-cli.js.

image

The full expression looks like this:

& C:\Program Files\nodejs/node.exe C:\Program Files\nodejs/node_modules/npm/bin/npm-cli.js run test

It does need the quoting 😉

@alexsch01
Copy link
Author

alexsch01 commented May 6, 2025

Thanks for the feedback! I'll look into the not so good part tomorrow

I also just made the ps1 files simpler by going back to the old logic if the grave key is found in the command....too many edge cases with that one

@alexsch01
Copy link
Author

Ok this PR looks complete to me, but if there's anything else to change please let me know

@wraithgar
Copy link
Member

Letting CI run. Will let @mbtools weigh in here before landing. Node 24 is now live with npm 11 so it will be good to get this landed.

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

Successfully merging this pull request may close these issues.

4 participants