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  }