From 10a0b53607b43b2c1d3e1c0467406bf5cb8cd18e Mon Sep 17 00:00:00 2001 From: Clayton Carter Date: Wed, 7 Nov 2018 05:24:31 -0500 Subject: [PATCH 1/4] add --search flag to lab issue list --- cmd/issue_list.go | 16 ++++++++++++++-- cmd/issue_list_test.go | 16 ++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/cmd/issue_list.go b/cmd/issue_list.go index ac46a24c..74d593bb 100644 --- a/cmd/issue_list.go +++ b/cmd/issue_list.go @@ -12,6 +12,7 @@ import ( var ( issueLabels []string issueState string + issueSearch string issueNumRet int issueAll bool ) @@ -35,6 +36,11 @@ var issueListCmd = &cobra.Command{ State: &issueState, OrderBy: gitlab.String("updated_at"), } + + if issueSearch != "" { + opts.Search = &issueSearch + } + num := issueNumRet if issueAll { num = -1 @@ -51,13 +57,19 @@ var issueListCmd = &cobra.Command{ func init() { issueListCmd.Flags().StringSliceVarP( - &issueLabels, "label", "l", []string{}, "Filter issues by label") + &issueLabels, "label", "l", []string{}, + "Filter issues by label") issueListCmd.Flags().StringVarP( &issueState, "state", "s", "opened", "Filter issues by state (opened/closed)") + issueListCmd.Flags().StringVarP( + &issueSearch, "search", "", "", + "Search project issues against their title and description") issueListCmd.Flags().IntVarP( &issueNumRet, "number", "n", 10, "Number of issues to return") - issueListCmd.Flags().BoolVarP(&issueAll, "all", "a", false, "List all issues on the project") + issueListCmd.Flags().BoolVarP( + &issueAll, "all", "a", false, + "List all issues on the project") issueCmd.AddCommand(issueListCmd) } diff --git a/cmd/issue_list_test.go b/cmd/issue_list_test.go index a565e4ba..b409c84e 100644 --- a/cmd/issue_list_test.go +++ b/cmd/issue_list_test.go @@ -55,3 +55,19 @@ func Test_issueListStateClosed(t *testing.T) { t.Log(issues) require.Contains(t, issues, "#4 test closed issue") } + +func Test_issueListSearch(t *testing.T) { + t.Parallel() + repo := copyTestRepo(t) + cmd := exec.Command("../lab_bin", "issue", "list", "--search", "filter labels") + cmd.Dir = repo + + b, err := cmd.CombinedOutput() + if err != nil { + t.Fatal(err) + } + + issues := strings.Split(string(b), "\n") + t.Log(issues) + require.Contains(t, issues, "#3 test filter labels 1") +} From 63c7f0112452dbf94c3f4685aaf954b91f08b615 Mon Sep 17 00:00:00 2001 From: Clayton Carter Date: Thu, 8 Nov 2018 22:04:27 -0500 Subject: [PATCH 2/4] refactor parseArgs, parseArgsStr; add parseArgsStringInt, parseArgsRemoteInt, parseArgsRemoteString --- cmd/root.go | 51 +++++++++++++++++++++++++++++++++++++ cmd/root_test.go | 65 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) diff --git a/cmd/root.go b/cmd/root.go index 3326aae8..d771cb55 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -110,6 +110,11 @@ func init() { // parseArgsStr returns a string and a number if parsed. Many commands accept a // string to operate on (remote or search) and number such as a page id func parseArgsStr(args []string) (string, int64, error) { + return parseArgsStringInt(args) +} + +// parseArgsStringInt returns a string and a number if parsed. +func parseArgsStringInt(args []string) (string, int64, error) { if len(args) == 2 { n, err := strconv.ParseInt(args[1], 0, 64) if err != nil { @@ -127,7 +132,14 @@ func parseArgsStr(args []string) (string, int64, error) { return "", 0, nil } +// parseArgs returns a remote name and a number if parsed func parseArgs(args []string) (string, int64, error) { + return parseArgsRemoteInt(args) +} + +// parseArgsRemoteInt is similar to parseArgsStringInt except that it uses the +// string argument as a remote and returns the project name for that remote +func parseArgsRemoteInt(args []string) (string, int64, error) { if !git.InsideGitRepo() { return "", 0, nil } @@ -156,6 +168,45 @@ func parseArgs(args []string) (string, int64, error) { return rn, num, nil } +// returns a remote name and a string if parsed +// returns two empty strings on error; if no remote is given, return project name +// for default remote (ie 'origin'); if no second argument is given, returns "" +func parseArgsRemoteString(args []string) (string, string, error) { + if !git.InsideGitRepo() { + return "", "", nil + } + + remote, str := forkedFromRemote, "" + + if len(args) == 1 { + ok, err := git.IsRemote(args[0]) + if err != nil { + return "", "", err + } + if ok { + remote = args[0] + } else { + str = args[0] + } + } else if len(args) > 1 { + remote, str = args[0], args[1] + } + + ok, err := git.IsRemote(remote) + if err != nil { + return "", "", err + } + if !ok { + return "", "", errors.Errorf("%s is not a valid remote", remote) + } + + remote, err = git.PathWithNameSpace(remote) + if err != nil { + return "", "", err + } + return remote, str, nil +} + var ( // Will be updated to upstream in Execute() if "upstream" remote exists forkedFromRemote = "origin" diff --git a/cmd/root_test.go b/cmd/root_test.go index fe6059ab..7cdc9dc5 100644 --- a/cmd/root_test.go +++ b/cmd/root_test.go @@ -329,3 +329,68 @@ func Test_parseArgs(t *testing.T) { }) } } + +func Test_parseArgsRemoteString(t *testing.T) { + tests := []struct { + Name string + Args []string + ExpectedRemote string + ExpectedString string + ExpectedErr string + }{ + { + Name: "No Args", + Args: nil, + ExpectedRemote: "zaquestion/test", + ExpectedString: "", + ExpectedErr: "", + }, + { + Name: "1 arg remote", + Args: []string{"lab-testing"}, + ExpectedRemote: "lab-testing/test", + ExpectedString: "", + ExpectedErr: "", + }, + { + Name: "1 arg non remote", + Args: []string{"foo123"}, + ExpectedRemote: "zaquestion/test", + ExpectedString: "foo123", + ExpectedErr: "", + }, + { + Name: "1 arg page", + Args: []string{"100"}, + ExpectedRemote: "zaquestion/test", + ExpectedString: "100", + ExpectedErr: "", + }, + { + Name: "2 arg remote and string", + Args: []string{"origin", "foo123"}, + ExpectedRemote: "zaquestion/test", + ExpectedString: "foo123", + ExpectedErr: "", + }, + { + Name: "2 arg invalid remote and string", + Args: []string{"foo", "string123"}, + ExpectedRemote: "", + ExpectedString: "", + ExpectedErr: "foo is not a valid remote", + }, + } + for _, test := range tests { + t.Run(test.Name, func(t *testing.T) { + test := test + t.Parallel() + r, s, err := parseArgsRemoteString(test.Args) + if err != nil { + assert.EqualError(t, err, test.ExpectedErr) + } + assert.Equal(t, test.ExpectedRemote, r) + assert.Equal(t, test.ExpectedString, s) + }) + } +} From 5379ce9087e19ee9486dda6075bffadeb11a0a0f Mon Sep 17 00:00:00 2001 From: Clayton Carter Date: Fri, 9 Nov 2018 03:04:33 +0000 Subject: [PATCH 3/4] remove --search, use positional arg for search terms instead (close 208) --- cmd/issue_list.go | 9 +++------ cmd/issue_list_test.go | 3 ++- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/cmd/issue_list.go b/cmd/issue_list.go index 74d593bb..d2baf27f 100644 --- a/cmd/issue_list.go +++ b/cmd/issue_list.go @@ -18,12 +18,12 @@ var ( ) var issueListCmd = &cobra.Command{ - Use: "list [remote]", - Aliases: []string{"ls"}, + Use: "list [remote] [search]", + Aliases: []string{"ls", "search"}, Short: "List issues", Long: ``, Run: func(cmd *cobra.Command, args []string) { - rn, _, err := parseArgs(args) + rn, issueSearch, err := parseArgsRemoteString(args) if err != nil { log.Fatal(err) } @@ -62,9 +62,6 @@ func init() { issueListCmd.Flags().StringVarP( &issueState, "state", "s", "opened", "Filter issues by state (opened/closed)") - issueListCmd.Flags().StringVarP( - &issueSearch, "search", "", "", - "Search project issues against their title and description") issueListCmd.Flags().IntVarP( &issueNumRet, "number", "n", 10, "Number of issues to return") diff --git a/cmd/issue_list_test.go b/cmd/issue_list_test.go index b409c84e..49258694 100644 --- a/cmd/issue_list_test.go +++ b/cmd/issue_list_test.go @@ -59,7 +59,7 @@ func Test_issueListStateClosed(t *testing.T) { func Test_issueListSearch(t *testing.T) { t.Parallel() repo := copyTestRepo(t) - cmd := exec.Command("../lab_bin", "issue", "list", "--search", "filter labels") + cmd := exec.Command("../lab_bin", "issue", "list", "filter labels") cmd.Dir = repo b, err := cmd.CombinedOutput() @@ -70,4 +70,5 @@ func Test_issueListSearch(t *testing.T) { issues := strings.Split(string(b), "\n") t.Log(issues) require.Contains(t, issues, "#3 test filter labels 1") + require.NotContains(t, issues, "#1 test issue for lab list") } From 845071d739294390551cceb27153cb51477f3c5e Mon Sep 17 00:00:00 2001 From: Clayton Carter Date: Tue, 13 Nov 2018 19:22:59 -0500 Subject: [PATCH 4/4] issue search: add examples and fix godoc comment --- cmd/issue_list.go | 4 ++++ cmd/root.go | 8 +++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/cmd/issue_list.go b/cmd/issue_list.go index d2baf27f..30d494b0 100644 --- a/cmd/issue_list.go +++ b/cmd/issue_list.go @@ -22,6 +22,10 @@ var issueListCmd = &cobra.Command{ Aliases: []string{"ls", "search"}, Short: "List issues", Long: ``, + Example: `lab issue list # list all open issues +lab issue list "search terms" # search issues for "search terms" +lab issue search "search terms" # same as above +lab issue list remote "search terms" # search "remote" for issues with "search terms"`, Run: func(cmd *cobra.Command, args []string) { rn, issueSearch, err := parseArgsRemoteString(args) if err != nil { diff --git a/cmd/root.go b/cmd/root.go index d771cb55..41dfabec 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -168,9 +168,11 @@ func parseArgsRemoteInt(args []string) (string, int64, error) { return rn, num, nil } -// returns a remote name and a string if parsed -// returns two empty strings on error; if no remote is given, return project name -// for default remote (ie 'origin'); if no second argument is given, returns "" +// parseArgsRemoteString returns a remote name and a string if parsed. +// If there is an error, it returns two empty strings. +// If no remote is given, it returns the project name of the default remote +// (ie 'origin'). +// If no second argument is given, it returns "" as second return value. func parseArgsRemoteString(args []string) (string, string, error) { if !git.InsideGitRepo() { return "", "", nil