-
Notifications
You must be signed in to change notification settings - Fork 50
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
cmd/analyze: use exit status 1 and 2 for errors, improve error messages for invalid cli arguments #967
Conversation
…flags, exit codes) Signed-off-by: Max Fisher <maxfisher@google.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the headline news of this change is that you're now returning a nonzero exit code if the command fails; that should probably be in the commit subject line.
cmd/analyze/main.go
Outdated
@@ -167,7 +168,7 @@ func staticAnalysis(ctx context.Context, pkg *pkgmanager.Pkg, resultStores *work | |||
} | |||
} | |||
|
|||
func main() { | |||
func run() (int, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a compelling reason to distinguish the usage errors vs other failures? The code gets a lot simpler if you just return either status 0 or 1.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, you could just return error
and leave the exit code decision up to main()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was just thinking of matching other unix utilities in this way. Since the options are quite complex for this program (and there is also an intermediate docker layer whose options are handled by a helper script), I think it helps for usability to distinguish user errors from program errors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is anything checking for the difference though?
If I saw "exit code 1" or "exit code 2" I'd still need to look at the other logs to figure out what went wrong.
I think unless another program is going to interpret your exit code in some meaningful way, it's better to err on the side of simplicity and just return 0 or 1.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair enough. I'll implement the error type as you suggested in the other comment but just return exit code 1 on any error until there's a clear need to return distinct error codes.
fair point, I'll add this to the final commit message and/or PR heading |
Signed-off-by: Max Fisher <maxfisher@google.com>
cmd/analyze/main.go
Outdated
@@ -23,6 +24,13 @@ import ( | |||
"github.com/ossf/package-analysis/pkg/api/pkgecosystem" | |||
) | |||
|
|||
const ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The more idiomatic way to do this in Go is to return an error and check for its type to determine the kind of error. In this case, you can declare a usageError
type and then return that, check for it in main, and return exit code 2 in that case. (for all other errors just return error code 1)
type usageError struct {
error
}
func usagef(format string, args ...any) error {
return usageError{fmt.Errorf(format, args...)}
}
func run() error {
...
if err := featureflags.Update(*features); err != nil {
return usagef("failed to parse flags: %w", err)
}
...
return nil
}
func main() {
if err := run(); err != nil {
fmt.Fprintln(os.Stderr, err)
if errors.As(err, &usageError{}) {
os.Exit(2)
}
os.Exit(1)
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the suggestion, this idiom is really nice! I'll implement it.
…or 1 Signed-off-by: Max Fisher <maxfisher@google.com>
…ror or not, streamline error message for invalid feature flag Signed-off-by: Max Fisher <maxfisher@google.com>
@@ -54,7 +54,7 @@ func Update(flags string) error { | |||
if ff, ok := flagRegistry[n]; ok { | |||
ff.isEnabled = isEnabled | |||
} else { | |||
return fmt.Errorf("%w: %s", ErrUndefinedFlag, n) | |||
return fmt.Errorf("%w '%s'", ErrUndefinedFlag, n) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use %q
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM modulo my last comments
… available ecosystems in usage text Signed-off-by: Max Fisher <maxfisher@google.com>
context
object initialisation