github.com/zaquestion/lab@v0.25.1/cmd/mr_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 mrLabels []string 18 mrState string 19 mrTargetBranch string 20 mrMilestone string 21 mrNumRet string 22 mrAll bool 23 mrMine bool 24 mrAuthor string 25 mrAuthorID *int 26 mrDraft bool 27 mrReady bool 28 mrConflicts bool 29 mrNoConflicts bool 30 mrExactMatch bool 31 mrAssignee string 32 mrAssigneeID *int 33 mrOrder string 34 mrSortedBy string 35 mrReviewer string 36 mrReviewerID *int 37 ) 38 39 // listCmd represents the list command 40 var listCmd = &cobra.Command{ 41 Use: "list [remote] [search]", 42 Aliases: []string{"ls", "search"}, 43 Short: "List merge requests", 44 Args: cobra.MaximumNArgs(2), 45 Example: heredoc.Doc(` 46 lab mr list 47 lab mr list "search terms" 48 lab mr list --target-branch main 49 lab mr list remote --target-branch main --label my-label 50 lab mr list -l bug 51 lab mr list -l close' 52 lab mr list upstream -n 5 53 lab mr list origin -a 54 lab mr list --author johndoe 55 lab mr list --assignee janedoe 56 lab mr list --order created_at 57 lab mr list --sort asc 58 lab mr list --draft 59 lab mr list --ready 60 lab mr list --no-conflicts 61 lab mr list -x 'test MR' 62 lab mr list -r johndoe`), 63 PersistentPreRun: labPersistentPreRun, 64 Run: func(cmd *cobra.Command, args []string) { 65 mrs, err := mrList(args) 66 if err != nil { 67 log.Fatal(err) 68 } 69 70 pager := newPager(cmd.Flags()) 71 defer pager.Close() 72 73 for _, mr := range mrs { 74 fmt.Printf("!%d %s\n", mr.IID, mr.Title) 75 } 76 }, 77 } 78 79 func mrList(args []string) ([]*gitlab.MergeRequest, error) { 80 rn, search, err := parseArgsRemoteAndProject(args) 81 if err != nil { 82 return nil, err 83 } 84 85 labels, err := mapLabels(rn, mrLabels) 86 if err != nil { 87 return nil, err 88 } 89 90 num, err := strconv.Atoi(mrNumRet) 91 if mrAll || (err != nil) { 92 num = -1 93 } 94 95 // gitlab lib still doesn't have search by assignee and author username 96 // for merge requests, because of that we need to get the ID for both. 97 if mrAssignee != "" { 98 mrAssigneeID = getUserID(mrAssignee) 99 if mrAssigneeID == nil { 100 log.Fatalf("%s user not found\n", mrAssignee) 101 } 102 } else if mrMine { 103 assigneeID, err := lab.UserID() 104 if err != nil { 105 log.Fatal(err) 106 } 107 mrAssigneeID = &assigneeID 108 } 109 110 if mrAuthor != "" { 111 mrAuthorID = getUserID(mrAuthor) 112 if mrAuthorID == nil { 113 log.Fatalf("%s user not found\n", mrAuthor) 114 } 115 } 116 117 if mrMilestone != "" { 118 milestone, err := lab.MilestoneGet(rn, mrMilestone) 119 if err != nil { 120 log.Fatal(err) 121 } 122 mrMilestone = milestone.Title 123 } 124 125 if mrReviewer != "" { 126 mrReviewerID = getUserID(mrReviewer) 127 if mrReviewerID == nil { 128 log.Fatalf("%s user not found\n", mrReviewer) 129 } 130 } 131 132 orderBy := gitlab.String(mrOrder) 133 134 sort := gitlab.String(mrSortedBy) 135 136 // if none of the flags are set, return every single MR 137 mrCheckConflicts := (mrConflicts || mrNoConflicts) 138 139 opts := gitlab.ListProjectMergeRequestsOptions{ 140 ListOptions: gitlab.ListOptions{ 141 PerPage: num, 142 }, 143 Labels: labels, 144 State: &mrState, 145 TargetBranch: &mrTargetBranch, 146 Milestone: &mrMilestone, 147 OrderBy: orderBy, 148 Sort: sort, 149 AuthorID: mrAuthorID, 150 AssigneeID: mrAssigneeID, 151 WithMergeStatusRecheck: gitlab.Bool(mrCheckConflicts), 152 ReviewerID: mrReviewerID, 153 } 154 155 if mrDraft && !mrReady { 156 opts.WIP = gitlab.String("yes") 157 } else if mrReady && !mrDraft { 158 opts.WIP = gitlab.String("no") 159 } 160 161 if mrExactMatch { 162 if search == "" { 163 return nil, errors.New("Exact match requested, but no search terms provided") 164 } 165 search = "\"" + search + "\"" 166 } 167 168 if search != "" { 169 opts.Search = &search 170 } 171 172 mrs, err := lab.MRList(rn, opts, num) 173 if err != nil { 174 return mrs, err 175 } 176 177 // only return MRs that matches the Conflicts requirement 178 if mrCheckConflicts { 179 var newMrList []*gitlab.MergeRequest 180 for _, mr := range mrs { 181 if mr.HasConflicts && mrConflicts { 182 newMrList = append(newMrList, mr) 183 } else if !mr.HasConflicts && mrNoConflicts { 184 newMrList = append(newMrList, mr) 185 } 186 } 187 mrs = newMrList 188 } 189 190 return mrs, nil 191 } 192 193 func init() { 194 listCmd.Flags().StringSliceVarP( 195 &mrLabels, "label", "l", []string{}, "filter merge requests by label") 196 listCmd.Flags().StringVarP( 197 &mrState, "state", "s", "opened", 198 "filter merge requests by state (all/opened/closed/merged)") 199 listCmd.Flags().StringVarP( 200 &mrNumRet, "number", "n", "10", 201 "number of merge requests to return") 202 listCmd.Flags().StringVarP( 203 &mrTargetBranch, "target-branch", "t", "", 204 "filter merge requests by target branch") 205 listCmd.Flags().StringVar( 206 &mrMilestone, "milestone", "", "list only MRs for the given milestone") 207 listCmd.Flags().BoolVarP(&mrAll, "all", "a", false, "list all MRs on the project") 208 listCmd.Flags().BoolVarP(&mrMine, "mine", "m", false, "list only MRs assigned to me") 209 listCmd.Flags().MarkDeprecated("mine", "use --assignee instead") 210 listCmd.Flags().StringVar(&mrAuthor, "author", "", "list only MRs authored by $username") 211 listCmd.Flags().StringVar( 212 &mrAssignee, "assignee", "", "list only MRs assigned to $username") 213 listCmd.Flags().StringVar(&mrOrder, "order", "updated_at", "display order (updated_at/created_at)") 214 listCmd.Flags().StringVar(&mrSortedBy, "sort", "desc", "sort order (desc/asc)") 215 listCmd.Flags().BoolVarP(&mrDraft, "draft", "", false, "list MRs marked as draft") 216 listCmd.Flags().BoolVarP(&mrReady, "ready", "", false, "list MRs not marked as draft") 217 listCmd.Flags().SortFlags = false 218 listCmd.Flags().BoolVar(&mrNoConflicts, "no-conflicts", false, "list only MRs that can be merged") 219 listCmd.Flags().BoolVar(&mrConflicts, "conflicts", false, "list only MRs that cannot be merged") 220 listCmd.Flags().BoolVarP(&mrExactMatch, "exact-match", "x", false, "match on the exact (case-insensitive) search terms") 221 listCmd.Flags().StringVar( 222 &mrReviewer, "reviewer", "", "list only MRs with reviewer set to $username") 223 224 mrCmd.AddCommand(listCmd) 225 carapace.Gen(listCmd).FlagCompletion(carapace.ActionMap{ 226 "label": carapace.ActionMultiParts(",", func(c carapace.Context) carapace.Action { 227 project, _, err := parseArgsRemoteAndProject(c.Args) 228 if err != nil { 229 return carapace.ActionMessage(err.Error()) 230 } 231 return action.Labels(project).Invoke(c).Filter(c.Parts).ToA() 232 }), 233 "milestone": carapace.ActionCallback(func(c carapace.Context) carapace.Action { 234 project, _, err := parseArgsRemoteAndProject(c.Args) 235 if err != nil { 236 return carapace.ActionMessage(err.Error()) 237 } 238 return action.Milestones(project, action.MilestoneOpts{Active: true}) 239 }), 240 "state": carapace.ActionValues("all", "opened", "closed", "merged"), 241 }) 242 243 carapace.Gen(listCmd).PositionalCompletion( 244 action.Remotes(), 245 ) 246 }