github.com/zaquestion/lab@v0.25.1/cmd/issue_list.go (about) 1 package cmd 2 3 import ( 4 "fmt" 5 "strconv" 6 7 "github.com/MakeNowJust/heredoc/v2" 8 "github.com/pkg/errors" 9 "github.com/rsteube/carapace" 10 "github.com/spf13/cobra" 11 gitlab "github.com/xanzy/go-gitlab" 12 "github.com/zaquestion/lab/internal/action" 13 lab "github.com/zaquestion/lab/internal/gitlab" 14 ) 15 16 var ( 17 issueLabels []string 18 issueMilestone string 19 issueState string 20 issueSearch string 21 issueNumRet string 22 issueAll bool 23 issueExactMatch bool 24 issueAssignee string 25 issueAssigneeID *int 26 issueAuthor string 27 issueAuthorID *int 28 issueOrder string 29 issueSortedBy string 30 ) 31 32 var issueListCmd = &cobra.Command{ 33 Use: "list [remote] [search]", 34 Aliases: []string{"ls", "search"}, 35 Short: "List issues", 36 Example: heredoc.Doc(` 37 lab issue list 38 lab issue list "search terms" 39 lab issue list origin "search terms" 40 lab issue list origin --all 41 lab issue list origin --assignee johndoe 42 lab issue list upstream --author janedoe 43 lab issue list upstream -x "An Issue with Abc" 44 lab issue list upstream -l "new_bug" 45 lab issue list upstream --milestone "week 22" 46 lab issue list remote -n "10" 47 lab issue list remote --order "created_at" 48 lab issue list remote --sort "asc" 49 lab issue list remote --state "closed"`), 50 PersistentPreRun: labPersistentPreRun, 51 Run: func(cmd *cobra.Command, args []string) { 52 issues, err := issueList(args) 53 if err != nil { 54 log.Fatal(err) 55 } 56 57 pager := newPager(cmd.Flags()) 58 defer pager.Close() 59 60 for _, issue := range issues { 61 fmt.Printf("#%d %s\n", issue.IID, issue.Title) 62 } 63 }, 64 } 65 66 func issueList(args []string) ([]*gitlab.Issue, error) { 67 rn, search, err := parseArgsRemoteAndProject(args) 68 if err != nil { 69 return nil, err 70 } 71 issueSearch = search 72 73 labels, err := mapLabels(rn, issueLabels) 74 if err != nil { 75 return nil, err 76 } 77 78 if issueMilestone != "" { 79 milestone, err := lab.MilestoneGet(rn, issueMilestone) 80 if err != nil { 81 return nil, err 82 } 83 issueMilestone = milestone.Title 84 } 85 86 num, err := strconv.Atoi(issueNumRet) 87 if issueAll || (err != nil) { 88 num = -1 89 } 90 91 // gitlab lib still doesn't have search by author username for issues, 92 // because of that we need to get user's ID for both assignee and 93 // author. 94 if issueAuthor != "" { 95 issueAuthorID = getUserID(issueAuthor) 96 if issueAuthorID == nil { 97 log.Fatalf("%s user not found\n", issueAuthor) 98 } 99 } 100 101 if issueAssignee != "" { 102 issueAssigneeID = getUserID(issueAssignee) 103 if issueAssigneeID == nil { 104 log.Fatalf("%s user not found\n", issueAssignee) 105 } 106 } 107 108 orderBy := gitlab.String(issueOrder) 109 110 sort := gitlab.String(issueSortedBy) 111 112 opts := gitlab.ListProjectIssuesOptions{ 113 ListOptions: gitlab.ListOptions{ 114 PerPage: num, 115 }, 116 Labels: labels, 117 Milestone: &issueMilestone, 118 State: &issueState, 119 OrderBy: orderBy, 120 Sort: sort, 121 AuthorID: issueAuthorID, 122 AssigneeID: issueAssigneeID, 123 } 124 125 if issueExactMatch { 126 if issueSearch == "" { 127 return nil, errors.New("Exact match requested, but no search terms provided") 128 } 129 issueSearch = "\"" + issueSearch + "\"" 130 } 131 132 if issueSearch != "" { 133 opts.Search = &issueSearch 134 } 135 136 return lab.IssueList(rn, opts, num) 137 } 138 139 func init() { 140 issueListCmd.Flags().StringSliceVarP( 141 &issueLabels, "label", "l", []string{}, 142 "filter issues by label") 143 issueListCmd.Flags().StringVarP( 144 &issueState, "state", "s", "opened", 145 "filter issues by state (all/opened/closed)") 146 issueListCmd.Flags().StringVarP( 147 &issueNumRet, "number", "n", "10", 148 "number of issues to return") 149 issueListCmd.Flags().BoolVarP( 150 &issueAll, "all", "a", false, 151 "list all issues on the project") 152 issueListCmd.Flags().StringVar( 153 &issueMilestone, "milestone", "", 154 "filter issues by milestone") 155 issueListCmd.Flags().StringVar( 156 &issueAssignee, "assignee", "", 157 "filter issues by assignee") 158 issueListCmd.Flags().StringVar( 159 &issueAuthor, "author", "", 160 "filter issues by author") 161 issueListCmd.Flags().BoolVarP( 162 &issueExactMatch, "exact-match", "x", false, 163 "match on the exact (case-insensitive) search terms") 164 issueListCmd.Flags().StringVar(&issueOrder, "order", "updated_at", "display order (updated_at/created_at)") 165 issueListCmd.Flags().StringVar(&issueSortedBy, "sort", "desc", "sort order (desc/asc)") 166 167 issueCmd.AddCommand(issueListCmd) 168 carapace.Gen(issueListCmd).FlagCompletion(carapace.ActionMap{ 169 "label": carapace.ActionMultiParts(",", func(c carapace.Context) carapace.Action { 170 project, _, err := parseArgsRemoteAndProject(c.Args) 171 if err != nil { 172 return carapace.ActionMessage(err.Error()) 173 } 174 return action.Labels(project).Invoke(c).Filter(c.Parts).ToA() 175 }), 176 "milestone": carapace.ActionCallback(func(c carapace.Context) carapace.Action { 177 project, _, err := parseArgsRemoteAndProject(c.Args) 178 if err != nil { 179 return carapace.ActionMessage(err.Error()) 180 } 181 return action.Milestones(project, action.MilestoneOpts{Active: true}) 182 }), 183 "state": carapace.ActionValues("all", "opened", "closed"), 184 }) 185 carapace.Gen(issueListCmd).PositionalCompletion( 186 action.Remotes(), 187 ) 188 }