3

I'm trying to write an automation Python script and I need to perform npm login as one of the steps. The npm login command prompts for password. I thought I could send it as a standard input, but it doesn't work:

proc = subprocess.run(['npm', 'login'],
                      encoding='utf-8',
                      text=True,
                      input='honzajavorek\npassword\n')
print(proc)

The code above exits with the following output:

Username: honzajavorek
Password: npm ERR! cb() never called!

npm ERR! This is an error with npm itself. Please report this error at:
npm ERR!     <https://npm.community>

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/honza/.npm/_logs/2020-09-02T13_45_56_798Z-debug.log
CompletedProcess(args=['npm', 'login'], returncode=1)

I thought I need to use Popen and be more interactive, but it actually got even worse. In the output above, you can see at least my username got entered, but the following code won't even manage that:

proc = subprocess.Popen(['npm', 'login'],
                        stdout=subprocess.PIPE,
                        stderr=subprocess.PIPE,
                        encoding='utf-8',
                        text=True,
                        stdin=subprocess.PIPE)

proc.stdin.write('honzajavorek\n')
proc.stdin.flush()
proc.stdin.write('password\n')
proc.stdin.flush()

stdout, stderr = proc.communicate()
print(stdout, stderr)

What am I doing wrong?

Honza Javorek
  • 8,566
  • 8
  • 47
  • 66
  • Try setting the flag `shell=True` in your `run` or `Popen` commands. Also can you use a .npmrc file to set your credentials? – Nihal Sangeeth Sep 02 '20 at 14:17
  • I tried the `shell` option, but no success. The `.npmrc` file might be an interesting path to take, I need to learn more about that. – Honza Javorek Sep 02 '20 at 14:44
  • You might consider using `npm-cli-login`, which lets you supply the password either as a command-line argument or an environment variable. Both have security issues, as the password would be visible to anyone that can view the processes running on the machine. – chepner Sep 07 '20 at 14:45

1 Answers1

3

npm login reads the password directly from the terminal (it explicitly opens /dev/pts/0), so passing data via stdin would not work. Use pexpect or similar tool that creates a new TTY for this reason.

Example of using expect script: https://stackoverflow.com/a/28110879/196206

Other solution: when stdout (yes, stdout, not stdin) is not a tty, the underlying read and readline library reads from stdin instead of terminal. So the Popen solution with stdout=PIPE would work. You just have to add some sleeps so it won't read the whole thing as a username.

import subprocess
from time import sleep

proc = subprocess.Popen(['npm', 'login'],
                        stdout=subprocess.PIPE,
                        stderr=subprocess.PIPE,
                        stdin=subprocess.PIPE)

sleep(1)
proc.stdin.write(b'honzajavorek\n')
proc.stdin.flush()
sleep(1)
proc.stdin.write(b'password\n')
proc.stdin.flush()
sleep(1)
proc.stdin.write(b'hello@example.com\n')
proc.stdin.flush()

stdout, stderr = proc.communicate()
print(stdout, stderr)
Messa
  • 24,321
  • 6
  • 68
  • 92