Skip to content

Commit a92e656

Browse files
refactor: return errors from registry constructors and introduce SemVersion pflag type
Registry constructors (check.NewRegistry, dependencies.NewRegistry, action.NewRegistry) now return (*Registry, error) instead of panicking via MustRegister, propagating registration errors to callers. Replace the TargetVersion string + parsedTargetVersion *semver.Version pair in the lint Command with a single version.SemVersion type that implements pflag.Value, validating at flag-parse time. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 39dcea0 commit a92e656

29 files changed

+509
-361
lines changed

cmd/lint/lint.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -67,25 +67,26 @@ const cmdExample = `
6767
`
6868

6969
// AddCommand adds the lint command to the root command.
70-
func AddCommand(root *cobra.Command, flags *genericclioptions.ConfigFlags) {
70+
func AddCommand(root *cobra.Command, flags *genericclioptions.ConfigFlags) error {
7171
streams := genericiooptions.IOStreams{
7272
In: root.InOrStdin(),
7373
Out: root.OutOrStdout(),
7474
ErrOut: root.ErrOrStderr(),
7575
}
7676

77-
// Create command with ConfigFlags from parent to ensure CLI auth flags are used
78-
command := lintpkg.NewCommand(streams, flags)
77+
command, err := lintpkg.NewCommand(streams, flags)
78+
if err != nil {
79+
return fmt.Errorf("initializing lint command: %w", err)
80+
}
7981

8082
cmd := &cobra.Command{
8183
Use: cmdName,
8284
Short: cmdShort,
8385
Long: cmdLong,
8486
Example: cmdExample,
8587
SilenceUsage: true,
86-
SilenceErrors: true, // We'll handle error output manually based on --quiet flag
88+
SilenceErrors: true,
8789
RunE: func(cmd *cobra.Command, _ []string) error {
88-
// Complete phase
8990
if err := command.Complete(); err != nil {
9091
if command.Verbose {
9192
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "Error: %v\n", err)
@@ -94,7 +95,6 @@ func AddCommand(root *cobra.Command, flags *genericclioptions.ConfigFlags) {
9495
return fmt.Errorf("completing command: %w", err)
9596
}
9697

97-
// Validate phase
9898
if err := command.Validate(); err != nil {
9999
if command.Verbose {
100100
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "Error: %v\n", err)
@@ -103,7 +103,6 @@ func AddCommand(root *cobra.Command, flags *genericclioptions.ConfigFlags) {
103103
return fmt.Errorf("validating command: %w", err)
104104
}
105105

106-
// Run phase
107106
err := command.Run(cmd.Context())
108107
if err != nil {
109108
if command.Verbose {
@@ -117,8 +116,9 @@ func AddCommand(root *cobra.Command, flags *genericclioptions.ConfigFlags) {
117116
},
118117
}
119118

120-
// Register flags using AddFlags method
121119
command.AddFlags(cmd.Flags())
122120

123121
root.AddCommand(cmd)
122+
123+
return nil
124124
}

cmd/main.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,14 @@ func main() {
2626
flags.AddFlags(cmd.PersistentFlags())
2727

2828
version.AddCommand(cmd, flags)
29-
lint.AddCommand(cmd, flags)
29+
30+
if err := lint.AddCommand(cmd, flags); err != nil {
31+
if _, writeErr := os.Stderr.WriteString(err.Error() + "\n"); writeErr != nil {
32+
os.Exit(1)
33+
}
34+
35+
os.Exit(1)
36+
}
3037

3138
if err := cmd.Execute(); err != nil {
3239
if _, writeErr := os.Stderr.WriteString(err.Error() + "\n"); writeErr != nil {

cmd/migrate/list/list.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package list
22

33
import (
4+
"fmt"
5+
46
"github.com/spf13/cobra"
57

68
"k8s.io/cli-runtime/pkg/genericclioptions"
@@ -40,8 +42,12 @@ func AddCommand(
4042
parent *cobra.Command,
4143
flags *genericclioptions.ConfigFlags,
4244
streams genericiooptions.IOStreams,
43-
) {
44-
command := migrate.NewListCommand(streams)
45+
) error {
46+
command, err := migrate.NewListCommand(streams)
47+
if err != nil {
48+
return fmt.Errorf("initializing list command: %w", err)
49+
}
50+
4551
command.ConfigFlags = flags
4652

4753
cmd := &cobra.Command{
@@ -67,4 +73,6 @@ func AddCommand(
6773

6874
command.AddFlags(cmd.Flags())
6975
parent.AddCommand(cmd)
76+
77+
return nil
7078
}

cmd/migrate/migrate.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package migrate
22

33
import (
4+
"fmt"
5+
46
"github.com/spf13/cobra"
57

68
"k8s.io/cli-runtime/pkg/genericclioptions"
@@ -54,7 +56,7 @@ const cmdExample = `
5456
`
5557

5658
// AddCommand adds the migrate command to the root command.
57-
func AddCommand(root *cobra.Command, flags *genericclioptions.ConfigFlags) {
59+
func AddCommand(root *cobra.Command, flags *genericclioptions.ConfigFlags) error {
5860
streams := genericiooptions.IOStreams{
5961
In: root.InOrStdin(),
6062
Out: root.OutOrStdout(),
@@ -70,9 +72,19 @@ func AddCommand(root *cobra.Command, flags *genericclioptions.ConfigFlags) {
7072
SilenceErrors: true,
7173
}
7274

73-
list.AddCommand(cmd, flags, streams)
74-
prepare.AddCommand(cmd, flags, streams)
75-
run.AddCommand(cmd, flags, streams)
75+
if err := list.AddCommand(cmd, flags, streams); err != nil {
76+
return fmt.Errorf("adding list subcommand: %w", err)
77+
}
78+
79+
if err := prepare.AddCommand(cmd, flags, streams); err != nil {
80+
return fmt.Errorf("adding prepare subcommand: %w", err)
81+
}
82+
83+
if err := run.AddCommand(cmd, flags, streams); err != nil {
84+
return fmt.Errorf("adding run subcommand: %w", err)
85+
}
7686

7787
root.AddCommand(cmd)
88+
89+
return nil
7890
}

cmd/migrate/prepare/prepare.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package prepare
22

33
import (
4+
"fmt"
5+
46
"github.com/spf13/cobra"
57

68
"k8s.io/cli-runtime/pkg/genericclioptions"
@@ -50,8 +52,12 @@ func AddCommand(
5052
parent *cobra.Command,
5153
flags *genericclioptions.ConfigFlags,
5254
streams genericiooptions.IOStreams,
53-
) {
54-
command := migrate.NewPrepareCommand(streams)
55+
) error {
56+
command, err := migrate.NewPrepareCommand(streams)
57+
if err != nil {
58+
return fmt.Errorf("initializing prepare command: %w", err)
59+
}
60+
5561
command.ConfigFlags = flags
5662

5763
cmd := &cobra.Command{
@@ -77,4 +83,6 @@ func AddCommand(
7783

7884
command.AddFlags(cmd.Flags())
7985
parent.AddCommand(cmd)
86+
87+
return nil
8088
}

cmd/migrate/run/run.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package run
22

33
import (
4+
"fmt"
5+
46
"github.com/spf13/cobra"
57

68
"k8s.io/cli-runtime/pkg/genericclioptions"
@@ -47,8 +49,12 @@ func AddCommand(
4749
parent *cobra.Command,
4850
flags *genericclioptions.ConfigFlags,
4951
streams genericiooptions.IOStreams,
50-
) {
51-
command := migrate.NewRunCommand(streams)
52+
) error {
53+
command, err := migrate.NewRunCommand(streams)
54+
if err != nil {
55+
return fmt.Errorf("initializing run command: %w", err)
56+
}
57+
5258
command.ConfigFlags = flags
5359

5460
cmd := &cobra.Command{
@@ -74,4 +80,6 @@ func AddCommand(
7480

7581
command.AddFlags(cmd.Flags())
7682
parent.AddCommand(cmd)
83+
84+
return nil
7785
}

docs/lint/architecture.md

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -82,43 +82,43 @@ func NewCommand(
8282
streams genericiooptions.IOStreams,
8383
configFlags *genericclioptions.ConfigFlags,
8484
options ...CommandOption,
85-
) *Command {
86-
registry := check.NewRegistry()
87-
88-
// Explicitly register all checks (no global state, full test isolation)
89-
// Components (13)
90-
registry.MustRegister(codeflare.NewRemovalCheck())
91-
registry.MustRegister(dashboard.NewAcceleratorProfileMigrationCheck())
92-
registry.MustRegister(dashboard.NewHardwareProfileMigrationCheck())
93-
registry.MustRegister(datasciencepipelines.NewInstructLabRemovalCheck())
94-
registry.MustRegister(datasciencepipelines.NewRenamingCheck())
95-
registry.MustRegister(kserve.NewServerlessRemovalCheck())
96-
registry.MustRegister(kserve.NewInferenceServiceConfigCheck())
97-
registry.MustRegister(kserve.NewServiceMeshOperatorCheck())
98-
registry.MustRegister(kserve.NewServiceMeshRemovalCheck())
99-
registry.MustRegister(kueue.NewManagementStateCheck())
100-
registry.MustRegister(kueue.NewOperatorInstalledCheck())
101-
registry.MustRegister(modelmesh.NewRemovalCheck())
102-
registry.MustRegister(trainingoperator.NewDeprecationCheck())
103-
104-
// Dependencies (2)
105-
registry.MustRegister(certmanager.NewCheck())
106-
registry.MustRegister(openshift.NewCheck())
107-
108-
// Workloads (8)
109-
registry.MustRegister(guardrails.NewImpactedWorkloadsCheck())
110-
registry.MustRegister(guardrails.NewOtelMigrationCheck())
111-
registry.MustRegister(kserveworkloads.NewAcceleratorMigrationCheck())
112-
registry.MustRegister(kserveworkloads.NewImpactedWorkloadsCheck())
113-
registry.MustRegister(notebook.NewAcceleratorMigrationCheck())
114-
registry.MustRegister(notebook.NewImpactedWorkloadsCheck())
115-
registry.MustRegister(ray.NewImpactedWorkloadsCheck())
116-
registry.MustRegister(trainingoperatorworkloads.NewImpactedWorkloadsCheck())
85+
) (*Command, error) {
86+
registry, err := check.NewRegistry(
87+
// Components (13)
88+
codeflare.NewRemovalCheck(),
89+
dashboard.NewAcceleratorProfileMigrationCheck(),
90+
dashboard.NewHardwareProfileMigrationCheck(),
91+
datasciencepipelines.NewInstructLabRemovalCheck(),
92+
datasciencepipelines.NewRenamingCheck(),
93+
kserve.NewServerlessRemovalCheck(),
94+
kserve.NewInferenceServiceConfigCheck(),
95+
kserve.NewServiceMeshOperatorCheck(),
96+
kserve.NewServiceMeshRemovalCheck(),
97+
kueue.NewManagementStateCheck(),
98+
kueue.NewOperatorInstalledCheck(),
99+
modelmesh.NewRemovalCheck(),
100+
trainingoperator.NewDeprecationCheck(),
101+
// Dependencies (2)
102+
certmanager.NewCheck(),
103+
openshift.NewCheck(),
104+
// Workloads (8)
105+
guardrails.NewImpactedWorkloadsCheck(),
106+
guardrails.NewOtelMigrationCheck(),
107+
kserveworkloads.NewAcceleratorMigrationCheck(),
108+
kserveworkloads.NewImpactedWorkloadsCheck(),
109+
notebook.NewAcceleratorMigrationCheck(),
110+
notebook.NewImpactedWorkloadsCheck(),
111+
ray.NewImpactedWorkloadsCheck(),
112+
trainingoperatorworkloads.NewImpactedWorkloadsCheck(),
113+
)
114+
if err != nil {
115+
return nil, fmt.Errorf("registering checks: %w", err)
116+
}
117117

118118
return &Command{
119119
SharedOptions: shared,
120120
registry: registry,
121-
}
121+
}, nil
122122
}
123123
```
124124

docs/lint/writing-checks.md

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,14 @@ func (c *Check) Validate(ctx context.Context, target check.Target) (*result.Diag
7979
}
8080
```
8181

82-
**Registration:** Checks are explicitly registered in `pkg/lint/command.go`:
82+
**Registration:** Checks are explicitly registered in `pkg/lint/command.go` via `check.NewRegistry(...)`:
8383

8484
```go
8585
// In NewCommand()
86-
registry.MustRegister(dashboard.NewCheck())
86+
registry, err := check.NewRegistry(
87+
dashboard.NewCheck(),
88+
// ... other checks
89+
)
8790
```
8891

8992
### Using BaseCheck
@@ -129,31 +132,31 @@ func NewCommand(
129132
streams genericiooptions.IOStreams,
130133
configFlags *genericclioptions.ConfigFlags,
131134
options ...CommandOption,
132-
) *Command {
133-
registry := check.NewRegistry()
134-
135-
// Explicitly register all checks
136-
// Components (11)
137-
registry.MustRegister(codeflare.NewRemovalCheck())
138-
registry.MustRegister(dashboard.NewAcceleratorProfileMigrationCheck())
139-
registry.MustRegister(dashboard.NewHardwareProfileMigrationCheck())
140-
registry.MustRegister(datasciencepipelines.NewInstructLabRemovalCheck())
141-
// ... additional component checks
142-
143-
// Dependencies (2)
144-
registry.MustRegister(certmanager.NewCheck())
145-
// ... additional dependency checks
146-
147-
// Workloads (7)
148-
registry.MustRegister(guardrails.NewOtelMigrationCheck())
149-
registry.MustRegister(kserveworkloads.NewAcceleratorMigrationCheck())
150-
registry.MustRegister(notebook.NewImpactedWorkloadsCheck())
151-
// ... additional workload checks
135+
) (*Command, error) {
136+
registry, err := check.NewRegistry(
137+
// Components (13)
138+
codeflare.NewRemovalCheck(),
139+
dashboard.NewAcceleratorProfileMigrationCheck(),
140+
dashboard.NewHardwareProfileMigrationCheck(),
141+
datasciencepipelines.NewInstructLabRemovalCheck(),
142+
// ... additional component checks
143+
// Dependencies (2)
144+
certmanager.NewCheck(),
145+
// ... additional dependency checks
146+
// Workloads (13)
147+
guardrails.NewOtelMigrationCheck(),
148+
kserveworkloads.NewAcceleratorMigrationCheck(),
149+
notebook.NewImpactedWorkloadsCheck(),
150+
// ... additional workload checks
151+
)
152+
if err != nil {
153+
return nil, fmt.Errorf("registering checks: %w", err)
154+
}
152155

153156
return &Command{
154157
SharedOptions: shared,
155158
registry: registry,
156-
}
159+
}, nil
157160
}
158161
```
159162

@@ -849,8 +852,8 @@ func (c *RemovalCheck) Validate(ctx context.Context, target check.Target) (*resu
849852
Run(ctx, validate.Removal("CodeFlare is enabled (state: %s) but will be removed in RHOAI 3.x"))
850853
}
851854

852-
// Registration is done explicitly in pkg/lint/command.go:
853-
// registry.MustRegister(codeflare.NewRemovalCheck())
855+
// Registration is done explicitly in pkg/lint/command.go via check.NewRegistry(...)
856+
// e.g. codeflare.NewRemovalCheck() is passed to check.NewRegistry()
854857
```
855858

856859
**Note:** For complex checks that don't fit the builder pattern, you can still write checks without builders using `c.NewResult()` and manually fetching resources via `client.GetDataScienceCluster(ctx, target.Client)`.

0 commit comments

Comments
 (0)