From 5a4003b6f9c81fc6063164e5f34435ed6eeb31db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emil=20W=C3=A5reus?= Date: Fri, 5 Jan 2024 16:44:36 +0100 Subject: [PATCH] initial implementation of resolution strictness with configurable status codes --- cmd/debricked/main.go | 9 +++++- internal/cmd/cmderror/error.go | 10 ++++++ internal/cmd/resolve/resolve.go | 22 ++++++------- internal/resolution/resolution.go | 12 +++++++ internal/resolution/resolver.go | 54 +++++++++++++++++++++++++++---- 5 files changed, 89 insertions(+), 18 deletions(-) create mode 100644 internal/cmd/cmderror/error.go diff --git a/cmd/debricked/main.go b/cmd/debricked/main.go index 3e4590b2..a58edee4 100644 --- a/cmd/debricked/main.go +++ b/cmd/debricked/main.go @@ -1,8 +1,10 @@ package main import ( + "errors" "os" + "github.com/debricked/cli/internal/cmd/cmderror" "github.com/debricked/cli/internal/cmd/root" "github.com/debricked/cli/internal/wire" ) @@ -13,6 +15,11 @@ var version string // Set at compile time func main() { if err := root.NewRootCmd(version, wire.GetCliContainer()).Execute(); err != nil { - os.Exit(1) + var cmdErr cmderror.CommandError + if errors.As(err, &cmdErr) { + os.Exit(cmdErr.Code) + } else { + os.Exit(1) + } } } diff --git a/internal/cmd/cmderror/error.go b/internal/cmd/cmderror/error.go new file mode 100644 index 00000000..47b8e8d2 --- /dev/null +++ b/internal/cmd/cmderror/error.go @@ -0,0 +1,10 @@ +package cmderror + +type CommandError struct { + Code int + Err error +} + +func (e CommandError) Error() string { + return e.Err.Error() +} diff --git a/internal/cmd/resolve/resolve.go b/internal/cmd/resolve/resolve.go index 140e108c..78c4f7ea 100644 --- a/internal/cmd/resolve/resolve.go +++ b/internal/cmd/resolve/resolve.go @@ -12,11 +12,11 @@ import ( ) var ( - exclusions = file.Exclusions() - verbose bool - npmPreferred bool - regenerate int - strictness int + exclusions = file.Exclusions() + verbose bool + npmPreferred bool + regenerate int + resolutionStrictness int ) const ( @@ -81,7 +81,7 @@ $ debricked resolve . `+exampleFlags) cmd.Flags().BoolP(NpmPreferredFlag, "", npmPreferred, npmPreferredDoc) - cmd.Flags().IntVarP(&strictness, ResolutionStrictFlag, "s", file.StrictAll, `Allows you to configure exit code 1 or 0 depending on if the resolution was successful or not. + cmd.Flags().IntVarP(&resolutionStrictness, ResolutionStrictFlag, "s", file.StrictAll, `Allows you to configure exit code 1 or 0 depending on if the resolution was successful or not. Strictness Level | Meaning ---------------- | ------- 0 (default) | Always exit with code 0, even if any or all files failed to resolve @@ -102,13 +102,13 @@ func RunE(resolver resolution.IResolver) func(_ *cobra.Command, args []string) e args = append(args, ".") } options := resolution.DebrickedOptions{ - Exclusions: viper.GetStringSlice(ExclusionFlag), - Verbose: viper.GetBool(VerboseFlag), - Regenerate: viper.GetInt(RegenerateFlag), - NpmPreferred: viper.GetBool(NpmPreferredFlag), + Exclusions: viper.GetStringSlice(ExclusionFlag), + Verbose: viper.GetBool(VerboseFlag), + Regenerate: viper.GetInt(RegenerateFlag), + NpmPreferred: viper.GetBool(NpmPreferredFlag), + Resolutionstrictness: viper.GetInt(ResolutionStrictFlag), } _, err := resolver.Resolve(args, options) - return err } } diff --git a/internal/resolution/resolution.go b/internal/resolution/resolution.go index 8d1781f9..04ffd6b9 100644 --- a/internal/resolution/resolution.go +++ b/internal/resolution/resolution.go @@ -5,6 +5,7 @@ import "github.com/debricked/cli/internal/resolution/job" type IResolution interface { Jobs() []job.IJob HasErr() bool + GetJobErrorCount() int } type Resolution struct { @@ -28,3 +29,14 @@ func (r Resolution) HasErr() bool { return false } + +func (r Resolution) GetJobErrorCount() int { + count := 0 + for _, j := range r.Jobs() { + if j.Errors().HasError() { + count++ + } + } + + return count +} diff --git a/internal/resolution/resolver.go b/internal/resolution/resolver.go index e5990c71..80960a99 100644 --- a/internal/resolution/resolver.go +++ b/internal/resolution/resolver.go @@ -2,10 +2,12 @@ package resolution import ( "errors" + "fmt" "os" "path" "regexp" + "github.com/debricked/cli/internal/cmd/cmderror" "github.com/debricked/cli/internal/file" resolutionFile "github.com/debricked/cli/internal/resolution/file" "github.com/debricked/cli/internal/resolution/job" @@ -32,11 +34,12 @@ type Resolver struct { type IOptions interface{} type DebrickedOptions struct { - Path string - Exclusions []string - Verbose bool - Regenerate int - NpmPreferred bool + Path string + Exclusions []string + Verbose bool + Regenerate int + NpmPreferred bool + Resolutionstrictness int } func NewResolver( @@ -58,6 +61,35 @@ func (r Resolver) setNpmPreferred(npmPreferred bool) { r.batchFactory.SetNpmPreferred(npmPreferred) } +func (r Resolver) GetExitCode(resolution IResolution, options DebrickedOptions) int { + errorCount := resolution.GetJobErrorCount() + jobCount := len(resolution.Jobs()) + + switch options.Resolutionstrictness { + case 0: + return 0 + case 1: + if errorCount == jobCount { + return 1 + } + return 0 + case 2: + if errorCount > 0 { + return 1 + } + return 0 + case 3: + if errorCount == 0 { + return 0 + } else if errorCount == jobCount { + return 1 + } + return 3 + default: + panic(fmt.Sprintf("Invalid strictness level: %d", options.Resolutionstrictness)) + } +} + func (r Resolver) Resolve(paths []string, options IOptions) (IResolution, error) { dOptions, ok := options.(DebrickedOptions) if !ok { @@ -86,7 +118,17 @@ func (r Resolver) Resolve(paths []string, options IOptions) (IResolution, error) if resolution.HasErr() { jobErrList := tui.NewJobsErrorList(os.Stdout, resolution.Jobs()) - err = jobErrList.Render(dOptions.Verbose) + renderErr := jobErrList.Render(dOptions.Verbose) + if renderErr != nil { + return resolution, renderErr + } + code := r.GetExitCode(resolution, dOptions) + if code != 0 { + err = cmderror.CommandError{ + Code: code, + Err: fmt.Errorf("resolution failed"), + } + } } return resolution, err