diff --git a/cmd/main.go b/cmd/main.go index 682fdc53d..abdd9c6bc 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -37,4 +37,4 @@ func main() { os.Exit(1) } commands.SendMetric(commands.ExecutionTime(startTime)) -} +} \ No newline at end of file diff --git a/pkg/cmd/add_repo.go b/pkg/cmd/add_repo.go index 956b37bd4..b709b44c8 100644 --- a/pkg/cmd/add_repo.go +++ b/pkg/cmd/add_repo.go @@ -24,6 +24,7 @@ import ( "github.com/spf13/cobra" "github.com/ZupIT/ritchie-cli/pkg/formula" + "github.com/ZupIT/ritchie-cli/pkg/formula/tree" "github.com/ZupIT/ritchie-cli/pkg/prompt" "github.com/ZupIT/ritchie-cli/pkg/rtutorial" "github.com/ZupIT/ritchie-cli/pkg/stdin" @@ -42,7 +43,8 @@ type addRepoCmd struct { prompt.InputList prompt.InputBool prompt.InputInt - rt rtutorial.Finder + tutorial rtutorial.Finder + tree tree.CheckerManager } func NewAddRepoCmd( @@ -55,6 +57,7 @@ func NewAddRepoCmd( inBool prompt.InputBool, inInt prompt.InputInt, rtf rtutorial.Finder, + treeChecker tree.CheckerManager, ) *cobra.Command { addRepo := addRepoCmd{ repo: repo, @@ -65,7 +68,8 @@ func NewAddRepoCmd( InputBool: inBool, InputInt: inInt, InputPassword: inPass, - rt: rtf, + tutorial: rtf, + tree: treeChecker, } cmd := &cobra.Command{ Use: "repo", @@ -180,11 +184,12 @@ func (ad addRepoCmd) runPrompt() CommandRunnerFunc { successMsg := fmt.Sprintf("The %q repository was added with success, now you can use your formulas with the Ritchie!", repository.Name) prompt.Success(successMsg) - tutorialHolder, err := ad.rt.Find() + tutorialHolder, err := ad.tutorial.Find() if err != nil { return err } tutorialAddRepo(tutorialHolder.Current) + ad.tree.Check() return nil } } @@ -206,7 +211,7 @@ func (ad addRepoCmd) runStdin() CommandRunnerFunc { successMsg := fmt.Sprintf("The %q repository was added with success, now you can use your formulas with the Ritchie!", r.Name) prompt.Success(successMsg) - tutorialHolder, err := ad.rt.Find() + tutorialHolder, err := ad.tutorial.Find() if err != nil { return err } diff --git a/pkg/cmd/add_repo_test.go b/pkg/cmd/add_repo_test.go index ff17c03af..890d0f2bb 100644 --- a/pkg/cmd/add_repo_test.go +++ b/pkg/cmd/add_repo_test.go @@ -21,6 +21,7 @@ import ( "testing" "github.com/ZupIT/ritchie-cli/pkg/formula" + "github.com/ZupIT/ritchie-cli/pkg/formula/tree" "github.com/ZupIT/ritchie-cli/pkg/git/github" "github.com/ZupIT/ritchie-cli/pkg/prompt" ) @@ -163,6 +164,8 @@ func Test_addRepoCmd_runPrompt(t *testing.T) { wantErr: true, }, } + checkerManager := tree.NewChecker(treeMock{}) + for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { o := NewAddRepoCmd( @@ -175,6 +178,7 @@ func Test_addRepoCmd_runPrompt(t *testing.T) { tt.fields.InputBool, tt.fields.InputInt, TutorialFinderMock{}, + checkerManager, ) o.PersistentFlags().Bool("stdin", false, "input by stdin") if err := o.Execute(); (err != nil) != tt.wantErr { diff --git a/pkg/cmd/create_formula.go b/pkg/cmd/create_formula.go index 5e5391433..01c0d5df9 100644 --- a/pkg/cmd/create_formula.go +++ b/pkg/cmd/create_formula.go @@ -30,6 +30,7 @@ import ( "github.com/ZupIT/ritchie-cli/pkg/api" "github.com/ZupIT/ritchie-cli/pkg/formula" "github.com/ZupIT/ritchie-cli/pkg/formula/creator/template" + "github.com/ZupIT/ritchie-cli/pkg/formula/tree" "github.com/ZupIT/ritchie-cli/pkg/prompt" "github.com/ZupIT/ritchie-cli/pkg/rtutorial" "github.com/ZupIT/ritchie-cli/pkg/stdin" @@ -44,7 +45,8 @@ type createFormulaCmd struct { inTextValidator prompt.InputTextValidator inList prompt.InputList tplM template.Manager - rt rtutorial.Finder + tutorial rtutorial.Finder + tree tree.CheckerManager } // CreateFormulaCmd creates a new cmd instance @@ -57,6 +59,7 @@ func NewCreateFormulaCmd( inTextValidator prompt.InputTextValidator, inList prompt.InputList, rtf rtutorial.Finder, + treeChecker tree.CheckerManager, ) *cobra.Command { c := createFormulaCmd{ homeDir: homeDir, @@ -66,7 +69,8 @@ func NewCreateFormulaCmd( inTextValidator: inTextValidator, inList: inList, tplM: tplM, - rt: rtf, + tutorial: rtf, + tree: treeChecker, } cmd := &cobra.Command{ @@ -130,7 +134,7 @@ func (c createFormulaCmd) runPrompt() CommandRunnerFunc { WorkspacePath: wspace.Dir, FormulaPath: formulaPath, } - + c.tree.Check() c.create(cf, wspace.Dir, formulaPath) return nil } @@ -171,7 +175,7 @@ func (c createFormulaCmd) create(cf formula.Create, workspacePath, formulaPath s return } - tutorialHolder, err := c.rt.Find() + tutorialHolder, err := c.tutorial.Find() if err != nil { s.Error(err) return diff --git a/pkg/cmd/create_formula_test.go b/pkg/cmd/create_formula_test.go index 645c459ff..31a47c670 100644 --- a/pkg/cmd/create_formula_test.go +++ b/pkg/cmd/create_formula_test.go @@ -22,6 +22,7 @@ import ( "testing" "github.com/ZupIT/ritchie-cli/pkg/formula/creator/template" + "github.com/ZupIT/ritchie-cli/pkg/formula/tree" "github.com/ZupIT/ritchie-cli/pkg/prompt" "github.com/ZupIT/ritchie-cli/pkg/stream" ) @@ -30,6 +31,7 @@ func TestNewCreateFormulaCmd(t *testing.T) { fileManager := stream.NewFileManager() dirManager := stream.NewDirManager(fileManager) tplM := template.NewManager("../../testdata", dirManager) + tChecker := tree.NewChecker(treeMock{}) cmd := NewCreateFormulaCmd( os.TempDir(), formCreator{}, @@ -39,6 +41,7 @@ func TestNewCreateFormulaCmd(t *testing.T) { inputTextValidatorMock{}, inputListMock{}, TutorialFinderMock{}, + tChecker, ) cmd.PersistentFlags().Bool("stdin", false, "input by stdin") if cmd == nil { @@ -125,6 +128,7 @@ func TestCreateFormulaCmd(t *testing.T) { tt.in.inTextValidator, tt.in.inList, TutorialFinderMock{}, + tree.CheckerManager{}, ) createFormulaCmd.PersistentFlags().Bool("stdin", false, "input by stdin") if err := createFormulaCmd.Execute(); (err != nil) != tt.wantErr { @@ -154,4 +158,4 @@ func (tm TemplateManagerCustomMock) ResolverNewPath(oldPath, newDir, lang, works } func (tm TemplateManagerCustomMock) Validate() error { return tm.ValidateMock() -} +} \ No newline at end of file diff --git a/pkg/cmd/mocks_test.go b/pkg/cmd/mocks_test.go index 7725760ba..5ad5ded93 100644 --- a/pkg/cmd/mocks_test.go +++ b/pkg/cmd/mocks_test.go @@ -20,11 +20,11 @@ import ( "errors" "io" + "github.com/spf13/cobra" + "github.com/ZupIT/ritchie-cli/pkg/api" "github.com/ZupIT/ritchie-cli/pkg/git" - "github.com/spf13/cobra" - "github.com/ZupIT/ritchie-cli/pkg/autocomplete" "github.com/ZupIT/ritchie-cli/pkg/credential" "github.com/ZupIT/ritchie-cli/pkg/formula" @@ -580,4 +580,4 @@ func (m RepositoryListUpdaterCustomMock) List() (formula.Repos, error) { func (m RepositoryListUpdaterCustomMock) Update(name formula.RepoName, version formula.RepoVersion) error { return m.update(name, version) -} +} \ No newline at end of file diff --git a/pkg/commands/builder.go b/pkg/commands/builder.go index f0de44a01..ee12fea09 100644 --- a/pkg/commands/builder.go +++ b/pkg/commands/builder.go @@ -121,6 +121,7 @@ func Build() *cobra.Command { credSetter := credential.NewSetter(ritchieHomeDir, ctxFinder) credFinder := credential.NewFinder(ritchieHomeDir, ctxFinder, fileManager) treeManager := tree.NewTreeManager(ritchieHomeDir, repoLister, api.CoreCmds, fileManager, repoProviders, isRootCommand) + treeChecker := tree.NewChecker(treeManager) credSettings := credential.NewSettings(fileManager, dirManager, userHomeDir) autocompleteGen := autocomplete.NewGenerator(treeManager) credResolver := envcredential.NewResolver(credFinder, credSetter, inputPassword) @@ -207,7 +208,7 @@ func Build() *cobra.Command { deleteCtxCmd := cmd.NewDeleteContextCmd(ctxFindRemover, inputBool, inputList) setCtxCmd := cmd.NewSetContextCmd(ctxFindSetter, inputText, inputList) showCtxCmd := cmd.NewShowContextCmd(ctxFinder) - addRepoCmd := cmd.NewAddRepoCmd(repoAddLister, repoProviders, inputTextValidator, inputPassword, inputURL, inputList, inputBool, inputInt, tutorialFinder) + addRepoCmd := cmd.NewAddRepoCmd(repoAddLister, repoProviders, inputTextValidator, inputPassword, inputURL, inputList, inputBool, inputInt, tutorialFinder, treeChecker) updateRepoCmd := cmd.NewUpdateRepoCmd(http.DefaultClient, repoListUpdater, repoProviders, inputText, inputPassword, inputURL, inputList, inputBool, inputInt) listRepoCmd := cmd.NewListRepoCmd(repoLister, repoProviders, tutorialFinder) deleteRepoCmd := cmd.NewDeleteRepoCmd(repoLister, inputList, repoDeleter) @@ -220,7 +221,7 @@ func Build() *cobra.Command { deleteWorkspaceCmd := cmd.NewDeleteWorkspaceCmd(userHomeDir, formulaWorkspace, dirManager, inputList, inputBool) deleteFormulaCmd := cmd.NewDeleteFormulaCmd(userHomeDir, ritchieHomeDir, formulaWorkspace, dirManager, inputBool, inputText, inputList, treeGen, fileManager) - createFormulaCmd := cmd.NewCreateFormulaCmd(userHomeDir, createBuilder, tplManager, formulaWorkspace, inputText, inputTextValidator, inputList, tutorialFinder) + createFormulaCmd := cmd.NewCreateFormulaCmd(userHomeDir, createBuilder, tplManager, formulaWorkspace, inputText, inputTextValidator, inputList, tutorialFinder, treeChecker) buildFormulaCmd := cmd.NewBuildFormulaCmd(userHomeDir, formulaLocalBuilder, formulaWorkspace, watchManager, dirManager, inputText, inputList, tutorialFinder) showFormulaRunnerCmd := cmd.NewShowFormulaRunnerCmd(configManager) setFormulaRunnerCmd := cmd.NewSetFormulaRunnerCmd(configManager, inputList) diff --git a/pkg/formula/tree.go b/pkg/formula/tree.go index cb4185cdb..4f7edd572 100644 --- a/pkg/formula/tree.go +++ b/pkg/formula/tree.go @@ -30,3 +30,7 @@ type TreeManager interface { type TreeGenerator interface { Generate(repoPath string) (Tree, error) } + +type TreeChecker interface { + Check() +} \ No newline at end of file diff --git a/pkg/formula/tree/checker.go b/pkg/formula/tree/checker.go new file mode 100644 index 000000000..27e72a844 --- /dev/null +++ b/pkg/formula/tree/checker.go @@ -0,0 +1,71 @@ +package tree + +import ( + "fmt" + "strings" + + "github.com/ZupIT/ritchie-cli/pkg/formula" + "github.com/ZupIT/ritchie-cli/pkg/prompt" +) + +type CheckerManager struct { + tree formula.TreeManager +} + +func NewChecker( + tree formula.TreeManager, +) CheckerManager { + return CheckerManager{ + tree: tree, + } +} + +// CheckCommands is used to warn the user about conflicting +// formula commands on different repos. This function doesn't +// return an error because printing an error from a unsuccessful +// warning attempt can be confusing to the user. +func (cm CheckerManager) Check() { + commands := cm.filterCommands() + conflictingCommands := cm.conflictingCommands(commands) + if len(conflictingCommands) >= 1 { + cm.printConflictingCommandsWarning(conflictingCommands) + } +} + +func (cm CheckerManager) filterCommands() []string { + var allCommands []string + tree, _ := cm.tree.Tree() + for _, t := range tree { + for _, c := range t.Commands { + if c.Formula { + allCommands = append(allCommands, c.Id) + } + } + } + return allCommands +} + +func (cm CheckerManager) conflictingCommands(commands []string) []string { + duplicateFrequency := make(map[string]int) + var duplicatedCommands []string + for _, command := range commands { + _, exist := duplicateFrequency[command] + if exist { + duplicateFrequency[command] += 1 + duplicatedCommands = append(duplicatedCommands, command) + } else { + duplicateFrequency[command] = 1 + } + } + return duplicatedCommands +} + +func (cm CheckerManager) printConflictingCommandsWarning(conflictingCommands []string) { + lastCommandIndex := len(conflictingCommands) - 1 + lastCommand := conflictingCommands[lastCommandIndex] + lastCommand = strings.Replace(lastCommand, "root", "rit", 1) + lastCommand = strings.ReplaceAll(lastCommand, "_", " ") + msg := fmt.Sprintf("There's a total of %d formula conflicting commands, like:\n %s", len(conflictingCommands), lastCommand) + msg = prompt.Yellow(msg) + fmt.Println(msg) +} diff --git a/pkg/formula/tree/checker_test.go b/pkg/formula/tree/checker_test.go new file mode 100644 index 000000000..44ca27ac7 --- /dev/null +++ b/pkg/formula/tree/checker_test.go @@ -0,0 +1,82 @@ +package tree + +import ( + "io/ioutil" + "os" + "strings" + "testing" + + "github.com/ZupIT/ritchie-cli/pkg/api" + "github.com/ZupIT/ritchie-cli/pkg/formula" +) + +func TestChecker(t *testing.T) { + treeMock := treeMock{ + tree: formula.Tree{ + Commands: api.Commands{ + { + Id: "root_mock", + Parent: "root", + Usage: "mock", + Help: "mock for add", + Formula: true, + }, + { + Id: "root_mock", + Parent: "root_mock", + Usage: "test", + Help: "test for add", + Formula: true, + }, + }, + }, + } + + tests := []struct { + name string + }{ + { + name: "Should warn conflicting commands", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + out := captureCheckerStdout(treeMock) + if !strings.Contains(out, "rit mock") { + t.Error("Wrong output on tree checker function") + } + }) + } +} + +func captureCheckerStdout(tree formula.TreeManager) string { + treeChecker := NewChecker(tree) + + rescueStdout := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + + treeChecker.Check() + + _ = w.Close() + out, _ := ioutil.ReadAll(r) + os.Stdout = rescueStdout + return string(out) +} + +type treeMock struct { + tree formula.Tree + error error + value string +} + +func (t treeMock) Tree() (map[string]formula.Tree, error) { + if t.value != "" { + return map[string]formula.Tree{t.value: t.tree}, t.error + } + return map[string]formula.Tree{"test": t.tree}, t.error +} + +func (t treeMock) MergedTree(bool) formula.Tree { + return t.tree +} diff --git a/pkg/formula/tree/default_tree.go b/pkg/formula/tree/default_tree.go index 0b6a77e84..373411102 100644 --- a/pkg/formula/tree/default_tree.go +++ b/pkg/formula/tree/default_tree.go @@ -166,4 +166,4 @@ func (d Manager) loadTree(treeCmdFile string) (formula.Tree, error) { } return tree, nil -} +} \ No newline at end of file