Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: migrate CLI to use cobra #114

Open
wants to merge 30 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
af0cdb0
chore: comment out the older cli code
1garo Feb 27, 2024
e35c42d
fix: move version cmd to other file
1garo Feb 27, 2024
78ed1ba
feat: add root command
1garo Feb 27, 2024
2918a69
feat: main file to call cmd.Execute()
1garo Feb 27, 2024
bdd67e1
feat: add cobra to the project
1garo Feb 27, 2024
7ad4826
feat: add flags the way cobra uses it
1garo Feb 27, 2024
d1bf455
feat: refactor to cobra style, seems to be working fine
1garo Feb 27, 2024
bc5123d
chore: default is not 0
1garo Feb 27, 2024
0f5f5e5
chore: add some comments to struct and functions
1garo Feb 27, 2024
e7dd29a
fix: refactor some of the functions
1garo Feb 28, 2024
c6cb3d3
feat(WIP): re-add tests
1garo Feb 28, 2024
6de0733
chore: remove old testing file
1garo Feb 29, 2024
36594c5
feat: add version cmd tests
1garo Feb 29, 2024
7550f12
feat: uncomment and fix other tests
1garo Feb 29, 2024
f2b2242
chore: run gofmt and remove comment
1garo Mar 8, 2024
21c4071
Merge branch 'main' into feat/migrate-to-cobra
1garo Apr 3, 2024
58a6988
chore: merge main
1garo Apr 3, 2024
7e1b9c5
chore: add quiet flag
1garo Apr 3, 2024
923d911
Update Dockerfile
1garo Apr 12, 2024
d581599
Update Dockerfile
1garo Apr 12, 2024
a561a84
Merge branch 'main' into feat/migrate-to-cobra
1garo Apr 12, 2024
66b4a3b
fix: do some cleanup after merge
1garo Apr 12, 2024
8a5c705
fix: properly resolve conflicts of last merge
1garo Apr 12, 2024
a9dddfe
chore: change from cmd/validator to main.go target
1garo Apr 12, 2024
08f83f6
chore: go mod tidy to update new deps
1garo Apr 12, 2024
4d73a9f
fix: add search_path as positional argument
1garo Apr 12, 2024
541f6f9
fix: add back quiet flag and update tests
1garo Apr 12, 2024
dad9045
chore: update readme target to main.go instead of cmd/validator.go
1garo Apr 12, 2024
e220a4a
chore: lint
1garo Apr 12, 2024
d78ef64
fix: update usage to cobra format and commands to use `--`
1garo Apr 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix: refactor some of the functions
  • Loading branch information
1garo committed Feb 28, 2024
commit e7dd29a34700d78a38b2a2908931e26cb414176b
33 changes: 14 additions & 19 deletions cmd/validator/commands/flags.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package cmd

import (
validator "github.com/Boeing/config-file-validator/cmd/validator"
"github.com/spf13/cobra"
)

//var depthPtr = flag.Int("depth", 0, "Depth of recursion for the provided search paths. Set depth to 0 to disable recursive path traversal")
1garo marked this conversation as resolved.
Show resolved Hide resolved
//var excludeDirsPtr = flag.String("exclude-dirs", "", "Subdirectories to exclude when searching for configuration files")

Expand All @@ -9,26 +14,16 @@ package cmd
//var versionPtr = flag.Bool("version", false, "Version prints the release version of validator")
//var groupOutputPtr = flag.String("groupby", "", "Group output by filetype, directory, pass-fail. Supported for Standard and JSON reports")

// ValidatorConfig holds all flag possible to be setted
type ValidatorConfig struct {
SearchPaths []string
Depth int
ExcludeDirs string
ExcludeFileTypes string
Output string
ReportType string
GroupOutput string
SearchPath string
func CmdFlags (cmd *cobra.Command) {
cmd.PersistentFlags().IntVar(&validator.Flags.Depth, "depth", 0, "Depth of recursion for the provided search paths. Set depth to 0 to disable recursive path traversal.")
cmd.PersistentFlags().StringVar(&validator.Flags.ExcludeDirs, "exclude-dirs", "", "Subdirectories to exclude when searching for configuration files")
cmd.PersistentFlags().StringVar(&validator.Flags.ExcludeFileTypes, "exclude-file-types", "", "A comma separated list of file types to ignore")
cmd.PersistentFlags().StringVar(&validator.Flags.Output, "output", "", "Destination to a file to output results")
cmd.PersistentFlags().StringVar(&validator.Flags.ReportType, "reporter", "standard", "Format of the printed report. Options are standard and json")
cmd.PersistentFlags().StringVar(&validator.Flags.GroupOutput, "groupby", "", "Group output by filetype, directory, pass-fail. Supported for Standard and JSON reports")
cmd.PersistentFlags().StringVar(&validator.Flags.SearchPath, "search_path", ".", "search_path: The search path on the filesystem for configuration files. Defaults to the current working directory if no search_path provided.")
}

var Flags ValidatorConfig

func init() {
rootCmd.PersistentFlags().IntVar(&Flags.Depth, "depth", 0, "Depth of recursion for the provided search paths. Set depth to 0 to disable recursive path traversal.")
rootCmd.PersistentFlags().StringVar(&Flags.ExcludeDirs, "exclude-dirs", "", "Subdirectories to exclude when searching for configuration files")
rootCmd.PersistentFlags().StringVar(&Flags.ExcludeFileTypes, "exclude-file-types", "", "A comma separated list of file types to ignore")
rootCmd.PersistentFlags().StringVar(&Flags.Output, "output", "", "Destination to a file to output results")
rootCmd.PersistentFlags().StringVar(&Flags.ReportType, "reporter", "standard", "Format of the printed report. Options are standard and json")
rootCmd.PersistentFlags().StringVar(&Flags.GroupOutput, "groupby", "", "Group output by filetype, directory, pass-fail. Supported for Standard and JSON reports")
rootCmd.PersistentFlags().StringVar(&Flags.SearchPath, "search_path", ".", "search_path: The search path on the filesystem for configuration files. Defaults to the current working directory if no search_path provided.")
CmdFlags(rootCmd)
}
175 changes: 2 additions & 173 deletions cmd/validator/commands/root.go
Original file line number Diff line number Diff line change
@@ -1,189 +1,18 @@
package cmd

import (
"errors"
"flag"
"fmt"
"log"
"os"
"slices"
"strings"

"github.com/Boeing/config-file-validator/pkg/cli"
"github.com/Boeing/config-file-validator/pkg/finder"
"github.com/Boeing/config-file-validator/pkg/reporter"
validator "github.com/Boeing/config-file-validator/cmd/validator"
"github.com/spf13/cobra"
)

//type ValidatorConfig struct {
// searchPaths []string
// excludeDirs *string
// excludeFileTypes *string
// reportType *string
// depth *int
// versionQuery *bool
// output *string
// groupby *string
//}

// Custom Usage function to cover
func validatorUsage() {
fmt.Printf("Usage: validator [OPTIONS] [<search_path>...]\n\n")
fmt.Printf("positional arguments:\n")
fmt.Printf(
" search_path: The search path on the filesystem for configuration files. " +
"Defaults to the current working directory if no search_path provided\n\n")
fmt.Printf("optional flags:\n")
flag.PrintDefaults()
}

// Parses, validates, and returns the flags
// flag.String returns a pointer
// If a required parameter is missing the help
// output will be displayed and the function
// will return with exit = 1
func getFlags(cmd *cobra.Command) (ValidatorConfig, error) {
depth := Flags.Depth
excludeDirs := Flags.ExcludeDirs
excludeFileTypes := Flags.ExcludeFileTypes
output := Flags.Output
reportType := Flags.ReportType
groupby := Flags.GroupOutput

searchPaths := make([]string, 0)

// If search path arg is empty, default is cwd (".")
// if not, set it to the arg. Supports N number of paths
searchPaths = append(searchPaths, Flags.SearchPath)

if reportType != "standard" && reportType != "json" && reportType != "junit" {
fmt.Println("Wrong parameter value for reporter, only supports standard, json or junit")
cmd.Usage()
return ValidatorConfig{}, errors.New("Wrong parameter value for reporter, only supports standard, json or junit")
}

if reportType == "junit" && groupby != "" {
fmt.Println("Wrong parameter value for reporter, groupby is not supported for JUnit reports")
cmd.Usage()
return ValidatorConfig{}, errors.New("Wrong parameter value for reporter, groupby is not supported for JUnit reports")
}

if isFlagSet("depth", cmd) && depth < 0 {
fmt.Println("Wrong parameter value for depth, value cannot be negative.")
cmd.Usage()
return ValidatorConfig{}, errors.New("Wrong parameter value for depth, value cannot be negative")
}

if groupby != "" {
groupByCleanString := cleanString(groupby)
groupByUserInput := strings.Split(groupByCleanString, ",")
groupByAllowedValues := []string{"filetype", "directory", "pass-fail"}
seenValues := make(map[string]bool)

// Check that the groupby values are valid and not duplicates
for _, groupBy := range groupByUserInput {
if !slices.Contains(groupByAllowedValues, groupBy) {
fmt.Println("Wrong parameter value for groupby, only supports filetype, directory, pass-fail")
cmd.Usage()
return ValidatorConfig{}, errors.New(
"Wrong parameter value for groupby, only supports filetype, directory, pass-fail",
)
}
if _, ok := seenValues[groupBy]; ok {
fmt.Println("Wrong parameter value for groupby, duplicate values are not allowed")
cmd.Usage()
return ValidatorConfig{}, errors.New("Wrong parameter value for groupby, duplicate values are not allowed")
}
seenValues[groupBy] = true
}
}

config := ValidatorConfig{
SearchPaths: searchPaths,
ExcludeDirs: excludeDirs,
ExcludeFileTypes: excludeFileTypes,
ReportType: reportType,
Depth: depth,
Output: output,
GroupOutput: groupby,
}

return config, nil
}

// isFlagSet verifies if a given flag has been set or not
func isFlagSet(flagName string, cmd *cobra.Command) bool {
return cmd.Flags().Lookup(flagName).Changed
}

// Return the reporter associated with the
// reportType string
func getReporter(reportType, outputDest string) reporter.Reporter {
switch reportType {
case "junit":
return reporter.NewJunitReporter(outputDest)
case "json":
return reporter.NewJsonReporter(outputDest)
default:
return reporter.StdoutReporter{}
}
}

// cleanString takes a command string and a split string
// and returns a cleaned string
func cleanString(str string) string {
str = strings.ToLower(str)
str = strings.TrimSpace(str)
return str
}

func execRoot(cmd *cobra.Command) int {
validatorConfig, err := getFlags(cmd)
if err != nil {
return 1
}

// since the exclude dirs are a comma separated string
// it needs to be split into a slice of strings
excludeDirs := strings.Split(validatorConfig.ExcludeDirs, ",")
reporter := getReporter(validatorConfig.ReportType, validatorConfig.Output)
excludeFileTypes := strings.Split(validatorConfig.ExcludeFileTypes, ",")
groupby := strings.Split(validatorConfig.GroupOutput, ",")
fsOpts := []finder.FSFinderOptions{
finder.WithPathRoots(validatorConfig.SearchPaths...),
finder.WithExcludeDirs(excludeDirs),
finder.WithExcludeFileTypes(excludeFileTypes),
}

if isFlagSet("depth", cmd) {
fsOpts = append(fsOpts, finder.WithDepth(validatorConfig.Depth))
}

// Initialize a file system finder
fileSystemFinder := finder.FileSystemFinderInit(fsOpts...)

// Initialize the CLI
cli := cli.Init(
cli.WithReporter(reporter),
cli.WithFinder(fileSystemFinder),
cli.WithGroupOutput(groupby),
)

// Run the config file validation
exitStatus, err := cli.Run()
if err != nil {
log.Printf("An error occurred during CLI execution: %v", err)
}

return exitStatus
}

// rootCmd command configuration and setup
var rootCmd = &cobra.Command{
Use: "validator",
Short: "Cross Platform tool to validate configuration files",
Run: func(cmd *cobra.Command, args []string) {
os.Exit(execRoot(cmd))
os.Exit(validator.ExecRoot(cmd))
},
}

Expand Down
Loading