Skip to content

Commit f40a355

Browse files
authored
🌱 add structured output from validation with 'output' option (#1971)
* add structured output from validation with 'output' option Signed-off-by: grokspawn <jordan@nimblewidget.com> * Address PR review feedback - Remove unused Errors field from ValidationError struct The field was never populated, making the structure misleading. Simplified to match actual implementation (single message). - Replace os.Exit with cobra error handling Use SilenceErrors/SilenceUsage instead of os.Exit to: - Allow deferred cleanup and hooks to run - Make command unit-testable and composable - Follow cobra best practices Exit code behavior verified unchanged (0=success, 1=failure) Addresses feedback from Copilot and tmshort on PR #1971 --------- Signed-off-by: grokspawn <jordan@nimblewidget.com>
1 parent bd6255d commit f40a355

1 file changed

Lines changed: 78 additions & 2 deletions

File tree

cmd/opm/validate/validate.go

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,50 @@
11
package validate
22

33
import (
4+
"encoding/json"
45
"fmt"
56
"os"
67

78
"github.com/sirupsen/logrus"
89
"github.com/spf13/cobra"
10+
"sigs.k8s.io/yaml"
911

1012
"github.com/operator-framework/operator-registry/pkg/lib/config"
1113
)
1214

15+
const (
16+
outputJSON = "json"
17+
outputYAML = "yaml"
18+
)
19+
20+
// ValidationResult represents the structured output of validation
21+
type ValidationResult struct {
22+
Passed bool `json:"passed" yaml:"passed"`
23+
Error *ValidationError `json:"error,omitempty" yaml:"error,omitempty"`
24+
}
25+
26+
// ValidationError represents a structured validation error
27+
type ValidationError struct {
28+
Message string `json:"message" yaml:"message"`
29+
}
30+
31+
// errorToValidationError converts a Go error into a structured ValidationError
32+
func errorToValidationError(err error) *ValidationError {
33+
if err == nil {
34+
return nil
35+
}
36+
37+
// For now, handle the error as a simple string message
38+
// The error tree structure is already formatted in the Error() output
39+
return &ValidationError{
40+
Message: err.Error(),
41+
}
42+
}
43+
1344
func NewCmd() *cobra.Command {
1445
logger := logrus.New()
46+
var output string
47+
1548
validate := &cobra.Command{
1649
Use: "validate <directory>",
1750
Short: "Validate the declarative index config",
@@ -27,12 +60,55 @@ func NewCmd() *cobra.Command {
2760
return fmt.Errorf("%q is not a directory", directory)
2861
}
2962

30-
if err := config.Validate(c.Context(), os.DirFS(directory)); err != nil {
31-
logger.Fatal(err)
63+
// Perform validation
64+
validationErr := config.Validate(c.Context(), os.DirFS(directory))
65+
66+
// Handle structured output
67+
if output != "" {
68+
result := ValidationResult{
69+
Passed: validationErr == nil,
70+
Error: errorToValidationError(validationErr),
71+
}
72+
73+
var data []byte
74+
var marshalErr error
75+
76+
switch output {
77+
case outputJSON:
78+
data, marshalErr = json.MarshalIndent(result, "", " ")
79+
case outputYAML:
80+
data, marshalErr = yaml.Marshal(result)
81+
default:
82+
return fmt.Errorf("invalid --output value %q, expected (json|yaml)", output)
83+
}
84+
85+
if marshalErr != nil {
86+
return fmt.Errorf("failed to marshal output: %w", marshalErr)
87+
}
88+
89+
if _, err := fmt.Fprintln(os.Stdout, string(data)); err != nil {
90+
return fmt.Errorf("failed to write output: %w", err)
91+
}
92+
93+
// Silence cobra error output only for validation errors
94+
// This prevents mixing structured output with error messages,
95+
// while still showing errors for flag/marshal/write failures
96+
if validationErr != nil {
97+
c.SilenceErrors = true
98+
c.SilenceUsage = true
99+
}
100+
return validationErr
101+
}
102+
103+
// Default behavior: use logger.Fatal on error
104+
if validationErr != nil {
105+
logger.Fatal(validationErr)
32106
}
33107
return nil
34108
},
35109
}
36110

111+
validate.Flags().StringVarP(&output, "output", "o", "", "Output format for validation results (json|yaml)")
112+
37113
return validate
38114
}

0 commit comments

Comments
 (0)