Skip to content

Command-line tools should adhere to stdout/stderr conventions #22878

@jasonkarns

Description

@jasonkarns

Problem

The problem is ef cli's unconventional stdout behavior which violates norms and expectations (coming from a *nix background, in particular). The downstream effects of this unconventional behavior are lack of interoperability with other tools and added friction when attempting to script the ef-cli.

Some example scenarios:

  1. Attempting to iterate over all migrations: for m in $(dotnet ef migrations list); do

    This command fails because the first two lines of output (on STDOUT) are Build started... and Build succeeded.. These lines are diagnostic output which should not be printed to STDOUT, but instead to STDERR. The only output on STDOUT from dotnet ef migrations list should be the actual list of migrations, such that the output can be piped or iterated over or consumed by other tooling.

  2. Attempting to generate the migration script: dotnet ef migrations script > migrate.sql

    This command likewise fails to produce the expected output because the first two lines of the output again are Build started... and Build succeeded.. I grant that this is a contrived example because, lo!, there exists the -o option! So some more background:

    A new user to this command might naively run: dotnet ef migrations script, and what do they see printed to STDOUT, but the desired SQL! And given that there is so much output, they would likely only see the last 50 or so lines of the sql script. Assuming that the command prints the SQL to STDOUT (because it does), they go about redirecting the output to a file, because that is the normal behavior on the CLI. Then this user might attempt to run the captured sql script, and encounter a sql syntax error because, unbeknownst to them, the first 2 lines of the file are not SQL, but instead diagnostic output from the dotnet build.

    But even aside from the first-time experience, there are still scenarios where it would be desirable to print the SQL script (and only the SQL script) to STDOUT. In particular, the generated script presently emits a BOM even though the file is UTF-8. (which is nonsensical, if common in .net tooling) Which means our enterprising developers who may use sql tools that choke on UTF-8 BOMs need to adjust the file before consuming it. A solution: dotnet ef migrations script | dos2unix > migrate.sql (or dotnet ef migrations script | strip-bom > migrate.sql. In this scenario, using the -o flag to capture the output is not sufficient.

  3. Invalid options cause the CLI to print "help/usage" output (yay!) but exit with a zero status code (boo). dotnet ef database -pMyProj -c CtxA update

    Eagle-eyes will notice that the -c/--context option is not a valid option to the database subcommand, but is instead only respected by the update sub-subcommand. Therefore the CLI "fails" by instead printing the CLI usage/help output. However, the CLI still exits with a zero (SUCCESS) status code. This scenario remained unnoticed on our CI server for weeks before anyone noticed that it was failing to fail correctly!!!

Expectation

  1. dotnet ef migrations list should print only the migration names to STDOUT; build output should go to STDERR
  2. dotnet ef migrations script should print only the migration SQL to STDOUT; build output should go to STDERR (and even better, should omit the BOM 😸 )
  3. Commands that fail should exit with a nonzero exit status!!! This includes commands that fail to run at all because they receive an unknown option or flag.
    1. (As a precautionary note: dotnet ef somecommand --help should still exit with a zero status code, because in that invocation, the user is explicitly requesting the help output. I've seen too many tools, when updated to exit nonzero when given unexpected arguments, suddenly start exiting nonzero whenever they print help/usage output. I hope that overcorrection doesn't happen here.)

Original thread: dotnet/sdk#7289 (comment)

Metadata

Metadata

Assignees

Type

No fields configured for Bug.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions