cosign icon indicating copy to clipboard operation
cosign copied to clipboard

Inappropriate printing to STDOUT

Open znewman01 opened this issue 3 years ago • 3 comments

Generally, in Cosign we try to make STDOUT have the output of the command: the (often machine-readable) result of execution. We put status messages, errors, etc. on STDERR.

This is a common convention for CLI tools and a nice thing to do for scriptability.

However, we're pretty sloppy sometimes.


I'm looking mostly at:

  • https://github.com/sigstore/cosign/blob/fa8a799784cafb0779849e69cb939c8dc6132a5e/pkg/cosign/tlog.go#L170
  • https://github.com/sigstore/cosign/blob/381ba64a577505d378b49482ee34d39eeab6ea50/cmd/cosign/cli/pivcli/commands.go#L152
  • https://github.com/sigstore/cosign/blob/381ba64a577505d378b49482ee34d39eeab6ea50/cmd/cosign/cli/initialize/init.go#L52
  • https://github.com/sigstore/cosign/blob/d720f040bf493861c1ea8280f462c14e705990e0/cmd/cosign/cli/sign/sign.go#L250

But even some of the ones that are actually correct just work by spitting out to STDOUT in the middle of command execution:

  • https://github.com/sigstore/cosign/blob/381ba64a577505d378b49482ee34d39eeab6ea50/cmd/cosign/cli/pivcli/commands.go#L265
  • https://github.com/sigstore/cosign/blob/d720f040bf493861c1ea8280f462c14e705990e0/cmd/cosign/cli/tree.go#L152

My proposal is the following:

  1. Have the CLI top-level entry point print the output of the command. That is, something like SaveCmd would be implemented as a function with a return value, and then we'd hook into Cobra to print the output (if we need to stream output, we could use channels or something).
  2. Ban (using Forbidigo) any calls to fmt.Print{f,ln} in the Cosign codebase.
  3. Implement a "Cosign output" package. Instead of fmt.Fprintln(os.Stderr, "warning: foo") directly, Cosign code would call Warn(). Same for any other common output patterns. (That is, calling code can assign semantics to its output which helps make sure it goes to the right place).
  4. Ban (most) other fmt.* calls (we can keep fmt.Sprint*).

This has a number of benefits:

  • Supporting multiple formats easily and consistently (you could pass --format=jsonl or --format=csv) without requiring support from every command
  • Testability: parsing STDOUT is hard. Right now that's part of the reason that the cmd/ directory is under-tested.
  • Enforce consistent output: of course, this fixes the STDOUT/STDERR thing. But it also starts to unify the output across the whole tool. If we wanted to start putting all STDERR output in red, we could do that in one place easily. If we want to "tee" all output to a log file, we can do that.

znewman01 avatar Dec 05 '22 17:12 znewman01

The way we handle this in kubectl and gitsign is by encapsulating the streams which has the added bonus of being really easy to mock in tests.

eddiezane avatar Dec 05 '22 19:12 eddiezane

The way we handle this in kubectl and gitsign is by encapsulating the streams which has the added bonus of being really easy to mock in tests.

Yeah I think this is also a good idea and can be implemented at the same time we do (3) above.

znewman01 avatar Dec 05 '22 19:12 znewman01

@znewman01 Is this issue still open?

krisharyan avatar Mar 09 '25 00:03 krisharyan