@@ -38,13 +38,21 @@ import (
3838 trainingoperatorworkloads "github.com/opendatahub-io/odh-cli/pkg/lint/checks/workloads/trainingoperator"
3939 "github.com/opendatahub-io/odh-cli/pkg/resources"
4040 "github.com/opendatahub-io/odh-cli/pkg/util/client"
41+ clierrors "github.com/opendatahub-io/odh-cli/pkg/util/errors"
4142 "github.com/opendatahub-io/odh-cli/pkg/util/iostreams"
4243 "github.com/opendatahub-io/odh-cli/pkg/util/version"
4344)
4445
4546// Verify Command implements cmd.Command interface at compile time.
4647var _ cmd.Command = (* Command )(nil )
4748
49+ const (
50+ msgProhibitedOrBlocking = "prohibited or blocking findings detected: upgrade cannot proceed"
51+ msgAdvisoryFindings = "advisory findings detected: review recommended before upgrade"
52+ msgInfrastructureErrors = "one or more checks failed due to infrastructure errors"
53+ msgCheckExecErrors = "check execution errors detected: %w"
54+ )
55+
4856// Command contains the lint command configuration.
4957type Command struct {
5058 * SharedOptions
@@ -248,8 +256,9 @@ func (c *Command) Run(ctx context.Context) error {
248256
249257 // Reject downgrades when explicit --target-version is provided
250258 if targetVersion .LT (* currentVersion ) {
251- return fmt .Errorf ("target version %s is older than current version %s (downgrades not supported)" ,
252- c .TargetVersion , currentVersion .String ())
259+ return clierrors .NewExitCodeError (clierrors .ExitValidation ,
260+ fmt .Errorf ("target version %s is older than current version %s (downgrades not supported)" ,
261+ c .TargetVersion , currentVersion .String ()))
253262 }
254263
255264 return c .runUpgradeMode (ctx , currentVersion )
@@ -315,8 +324,25 @@ func (c *Command) runUpgradeMode(ctx context.Context, currentVersion *semver.Ver
315324 resultsByGroup [group ] = results
316325 }
317326
318- // Flatten, strip nil results, and apply severity filter
327+ // Flatten results and compute the highest-priority exit code from execution
328+ // errors BEFORE filtering, so failures with Result == nil are not dropped.
319329 flatResults := FlattenResults (resultsByGroup )
330+
331+ execExitCode := clierrors .ExitSuccess
332+
333+ var firstExecErr error
334+
335+ for _ , exec := range flatResults {
336+ if exec .Error != nil {
337+ code := clierrors .ExitCodeFromError (exec .Error )
338+ if clierrors .IsHigherPriority (code , execExitCode ) {
339+ execExitCode = code
340+ firstExecErr = exec .Error
341+ }
342+ }
343+ }
344+
345+ // Strip nil results and apply severity filter for display/verdict
320346 flatResults = slices .DeleteFunc (flatResults , func (exec check.CheckExecution ) bool {
321347 return exec .Result == nil
322348 })
@@ -327,13 +353,37 @@ func (c *Command) runUpgradeMode(ctx context.Context, currentVersion *semver.Ver
327353 return err
328354 }
329355
330- // Print verdict and determine exit code
331- return c .printVerdictAndExit (flatResults )
356+ // Print verdict and determine exit code from findings
357+ findingsErr := c .evaluateVerdict (flatResults )
358+
359+ // If check execution errors (e.g. auth, connection) have higher priority
360+ // than findings, propagate the infrastructure exit code instead.
361+ if execExitCode != clierrors .ExitSuccess {
362+ findingsExitCode := clierrors .ExitCodeFromError (findingsErr )
363+ if clierrors .IsHigherPriority (execExitCode , findingsExitCode ) ||
364+ execExitCode == findingsExitCode {
365+ if findingsErr != nil {
366+ return clierrors .NewExitCodeError (execExitCode ,
367+ fmt .Errorf (msgCheckExecErrors , firstExecErr ))
368+ }
369+
370+ return clierrors .NewExitCodeError (execExitCode ,
371+ fmt .Errorf (msgInfrastructureErrors + ": %w" , firstExecErr ))
372+ }
373+ }
374+
375+ // Verdict errors are pure exit code signals — the findings have already
376+ // been rendered by formatAndOutputUpgradeResults above.
377+ if findingsErr != nil {
378+ return clierrors .NewAlreadyHandledError (findingsErr ) //nolint:wrapcheck // wrapping is done by NewAlreadyHandledError
379+ }
380+
381+ return nil
332382}
333383
334- // printVerdictAndExit prints a prominent result verdict for table output and returns
335- // an error if fail-on conditions are met (to control exit code) .
336- func (c * Command ) printVerdictAndExit (results []check.CheckExecution ) error {
384+ // evaluateVerdict prints a prominent result verdict for table output and returns
385+ // an error carrying the appropriate ExitCode when fail-on conditions are met.
386+ func (c * Command ) evaluateVerdict (results []check.CheckExecution ) error {
337387 var hasProhibited , hasBlocking , hasAdvisory bool
338388
339389 for _ , exec := range results {
@@ -357,8 +407,18 @@ func (c *Command) printVerdictAndExit(results []check.CheckExecution) error {
357407 printVerdict (c .IO .Out (), hasProhibited , hasBlocking , hasAdvisory )
358408 }
359409
360- if hasProhibited {
361- return errors .New ("prohibited findings detected: upgrade is not possible" )
410+ if hasProhibited || hasBlocking {
411+ return clierrors .NewExitCodeError (
412+ clierrors .ExitError ,
413+ errors .New (msgProhibitedOrBlocking ),
414+ )
415+ }
416+
417+ if hasAdvisory {
418+ return clierrors .NewExitCodeError (
419+ clierrors .ExitWarning ,
420+ errors .New (msgAdvisoryFindings ),
421+ )
362422 }
363423
364424 return nil
0 commit comments