1. 7
    1. 8

      I expect: […]

      • exit code 2 on warnings

      A warning is recoverable, right? Otherwise it would be an error. So why use a non-zero exit code when the command didn’t fail? I’ve seen this pattern elsewhere, and I don’t understand it. Exit codes are limited to a single byte of information, and if you want to communicate some non-fatal issue to the user that can be done in great detail using a warning message, but please don’t treat a non-failure as a failure. That just leads to ugly hacks like puppet || [[ $? -eq 2 ]] or worse to check for non-errors.

      1. 3

        I makes sense if you document the exit codes accordingly and scripts around the utility you are providing can take care of them, rather than parsing output messages. take rsync, for example, exit codes:

        0 Success 1 Syntax or usage error 2 Protocol incompatibility 3 Errors selecting input/output files, dirs 4 Requested action not supported: an attempt was made to manipulate 64-bit files on a platform that cannot support them; or an option was specified that is supported by the client and not by the server. [ and many more ]

        depends on the situation of course. A warning during file copy might not be “recoverable” in the sense of: the file on the target is still ok. Or maybe just ACL information missing? Or Metadata?

    2. 2

      In Go, I wrote something that smuggles exitcodes into error values: https://blog.carlmjohnson.net/post/2020/working-with-errors-as/

    3. 2

      Nice article!

      One minor suggestion: I might consider using a dictionary in emit() rather than the chained if/else.

      That way you also win more flexibility to include other levels without adding new cnoditionals to your code.