diff --git a/README.md b/README.md index db2a987..d183f38 100644 --- a/README.md +++ b/README.md @@ -935,10 +935,11 @@ export PLUMBER_NO_UPDATE_CHECK=1 ### Exit Codes -| Code | Meaning | -|------|---------| -| `0` | Compliance ≥ threshold | -| `1` | Compliance < threshold or error | +| Exit Code | Meaning | +|-----------|----------| +| `0` | Analysis passed (compliance ≥ threshold) | +| `1` | Compliance failure (compliance < threshold) | +| `2` | Runtime error (config error, network failure, missing token, etc.) | ### `plumber config generate` diff --git a/cmd/analyze.go b/cmd/analyze.go index f3ceb43..c76900b 100644 --- a/cmd/analyze.go +++ b/cmd/analyze.go @@ -69,7 +69,8 @@ Optional flags: Exit codes: 0 Analysis passed (compliance >= threshold) - 1 Analysis failed (compliance < threshold or error occurred) + 1 Compliance failure (compliance < threshold) + 2 Runtime error (configuration error, network failure, missing token, etc.) Examples: # Set token via environment variable @@ -403,7 +404,7 @@ func runAnalyze(cmd *cobra.Command, args []string) error { // Check compliance against threshold if compliance < threshold { - return fmt.Errorf("compliance %.1f%% is below threshold %.1f%%", compliance, threshold) + return &ComplianceError{Compliance: compliance, Threshold: threshold} } return nil diff --git a/cmd/errors.go b/cmd/errors.go new file mode 100644 index 0000000..d439caa --- /dev/null +++ b/cmd/errors.go @@ -0,0 +1,21 @@ +package cmd + +import "fmt" + +// ComplianceError is returned when analysis completes successfully but the +// measured compliance score falls below the required threshold. +// +// It is distinct from a generic runtime error so that callers (e.g. Execute) +// can map it to a dedicated exit code: +// +// 0 – analysis passed (compliance ≥ threshold) +// 1 – compliance failure (compliance < threshold) +// 2 – runtime / configuration error +type ComplianceError struct { + Compliance float64 + Threshold float64 +} + +func (e *ComplianceError) Error() string { + return fmt.Sprintf("compliance %.1f%% is below threshold %.1f%%", e.Compliance, e.Threshold) +} diff --git a/cmd/root.go b/cmd/root.go index 6419569..3b5fe9a 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -1,6 +1,7 @@ package cmd import ( + "errors" "fmt" "os" "time" @@ -48,7 +49,11 @@ func Execute() { } if err != nil { - os.Exit(1) + var complianceErr *ComplianceError + if errors.As(err, &complianceErr) { + os.Exit(1) + } + os.Exit(2) } }