Description
The STEP CLI is not easily used in a non-interactive (i.e. scripted) fashion, since it seems to assume a human-in-the-loop.
What would you like to be added
It's not clear what the "best" solution is to the problem, fully described below. I see several possible options:
- CLI should provide command line option or environment variable equivalents for all TTY prompts,
- CLI should have a flag, say
--non-interactive
, never prompting the user and accepting all 'defaults', - CLI should accept input on STDIN for all prompts (admittedly brittle).
Why this is needed
As currently implemented, step-cli
looks to be coded to open the TTY directly to ask questions of the user, assuming there is a human running the script. Moreover, there is no option to say --non-interactive
(i.e. --don't-prompt-me--I'm-not-human
), nor is there a way to provide answers to the all the questions that may be asked. This is a problem when trying to build automation around the cli, using Ansible, for example.
One very (POSIX) traditional, albeit brittle, way of providing input to a CLI would be to pipe the answers in via STDIN, in a pipeline fashion. Looking briefly through the code base, it looks like the use of the ui.Prompt()
is pervasive, which is great from a consistency perspective, but that function doesn't pay any attention to STDIN, and attempts to directly interact with the underlying TTY.
Another way would be to ensure that all questions have corresponding command line options or environment variable equivalents that can be used if there is no TTY from which to read. Perhaps the most uniform way would be to uniquely "TAG" all the questions being asked. Then a generic -o TAG=value
command line option could be added. This would also be amenable to allowing STEPCLI_TAG=value step-cli ...
environment variable equivalents. Most of the functionality for this would be implemented directly in ui.go
, thus minimizing pervasive changes.
The final approach I can envision, and probably the easiest to implement, would be to add a --non-interactive
option to the CLI, which would be used in the ui/ui.go
function ui.Prompt()
to avoid even the attempt to open the TTY and ask a question, instead returning the default response directly as the answer.
Honestly, that last approach could probably be assumed if there is no TTY available, thus no additional command-line option (e.g. --non-interactive
) is needed at all.
Example Use Case
My goal is to automate use of the step-cli
. This most recently came up because I am deploying a new CA, replacing an existing CA, and therefore my current Ansible playbooks (i.e. scripts) are trying to issue a command like this:
step-cli ca bootstrap --ca-url=https://HOSTNAME:443 --fingerprint=HASH
If I issue that command by hand, I get a nice prompt:
✗ Would you like to overwrite /root/.step/certs/root_ca.crt [y/n]: █
However, in the case of the automation, this fails with the above no /dev/tty
error. Attempts to close stdin
or do yes no | step-cli ca ...
have no effect, obviously, since the cli
doesn't even look at STDIN.
I know I can manually remove the config file or directory before running the command, or use the --force
option, but in this case, I want to get an error (return code != 0) if the host thinks it's already got a CA configured, and then have the automation report that to the user, or do something smarter. Ideally, the answer no
could be provided to the CLI, in which case I would get exactly what I want, just like as if I run the command by hand:
root@steptest # step-cli ca bootstrap --ca-url=https://HOST:443 --fingerprint=HASH
✔ Would you like to overwrite /root/.step/certs/root_ca.crt [y/n]: n
unexpected error on /root/.step/certs/root_ca.crt: file exists
root@steptest # echo $?
1
However, as it is currently, when running that command via my scripts, this fails with an unhelpful error:
open /dev/tty: no such device or address
Note that the fundamental problem is much bigger than my example here. IMO, anywhere the cli
would issue a prompt to a presumed human user should be avoidable by some means employable by scripts/ CI / automation.
Related Issues
I believe versions of this problem have been discussed before, in one or more of these issues/PRs:
- Switch prompts to the ui package #39
- Don't open a prompt terminal if the option for the prompt is already set via the cli. #75
- JWS Sign doesn't use stdin content #109
- Read from stdin if no filename is given #171
- use isatty() before formatting -h text #408
- Make empty STDIN work for
crypto jwt sign
when no input is specified #494