This repository has been archived by the owner on Nov 22, 2022. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 163
/
mrutils.go
232 lines (200 loc) · 6.82 KB
/
mrutils.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
package mrutils
import (
"context"
"fmt"
"strconv"
"strings"
"github.com/profclems/glab/commands/cmdutils"
"github.com/profclems/glab/internal/glrepo"
"github.com/profclems/glab/internal/utils"
"github.com/profclems/glab/pkg/api"
"github.com/profclems/glab/pkg/tableprinter"
"github.com/xanzy/go-gitlab"
"golang.org/x/sync/errgroup"
)
type MRCheckErrOptions struct {
// WorkInProgress: check and return err if merge request is a DRAFT
WorkInProgress bool
// Closed : check and return err if merge request is closed
Closed bool
// Merged : check and return err if merge request is already merged
Merged bool
// Opened : check and return err if merge request is already opened
Opened bool
// Conflict : check and return err if there are merge conflicts
Conflict bool
// PipelineStatus : check and return err pipeline did not succeed and it is required before merging
PipelineStatus bool
// MergePermitted : check and return err if user is not authorized to merge
MergePermitted bool
// Subscribed : check and return err if user is already subscribed to MR
Subscribed bool
// Unsubscribed : check and return err if user is already unsubscribed to MR
Unsubscribed bool
// MergePrivilege : check and return err if user is not authorized to merge
MergePrivilege bool
}
// MRCheckErrors checks and return merge request errors specified in MRCheckErrOptions{}
func MRCheckErrors(mr *gitlab.MergeRequest, err MRCheckErrOptions) error {
if mr.WorkInProgress && err.WorkInProgress {
return fmt.Errorf("this merge request is still a work in progress. Run `glab mr update %d --ready` to mark it as ready for review", mr.IID)
}
if mr.MergeWhenPipelineSucceeds && err.PipelineStatus && mr.Pipeline != nil {
if mr.Pipeline.Status != "success" {
return fmt.Errorf("pipeline for this merge request has failed. Pipeline is required to succeed before merging")
}
}
if mr.State == "merged" && err.Merged {
return fmt.Errorf("this merge request has already been merged")
}
if mr.State == "closed" && err.Closed {
return fmt.Errorf("this merge request has been closed")
}
if mr.State == "opened" && err.Opened {
return fmt.Errorf("this merge request is already open")
}
if mr.Subscribed && err.Subscribed {
return fmt.Errorf("you are already subscribed to this merge request")
}
if !mr.Subscribed && err.Unsubscribed {
return fmt.Errorf("you are already unsubscribed to this merge request")
}
if err.MergePrivilege && !mr.User.CanMerge {
return fmt.Errorf("you do not have enough priviledges to merge this merge request")
}
if err.Conflict && mr.HasConflicts {
return fmt.Errorf("there are merge conflicts. Resolve conflicts and try again or merge locally")
}
return nil
}
func DisplayMR(mr *gitlab.MergeRequest) string {
mrID := MRState(mr)
return fmt.Sprintf("%s %s (%s)\n %s\n",
mrID, mr.Title, mr.SourceBranch, mr.WebURL)
}
func MRState(m *gitlab.MergeRequest) string {
if m.State == "opened" {
return utils.Green(fmt.Sprintf("!%d", m.IID))
} else if m.State == "merged" {
return utils.Blue(fmt.Sprintf("!%d", m.IID))
} else {
return utils.Red(fmt.Sprintf("!%d", m.IID))
}
}
func DisplayAllMRs(mrs []*gitlab.MergeRequest, projectID string) string {
table := tableprinter.NewTablePrinter()
for _, m := range mrs {
table.AddCell(MRState(m))
table.AddCell(m.Title)
table.AddCell(utils.Cyan(fmt.Sprintf("(%s) ← (%s)", m.TargetBranch, m.SourceBranch)))
table.EndRow()
}
return table.Render()
}
//MRFromArgs is wrapper around MRFromArgsWithOpts without any custom options
func MRFromArgs(f *cmdutils.Factory, args []string) (*gitlab.MergeRequest, glrepo.Interface, error) {
return MRFromArgsWithOpts(f, args, &gitlab.GetMergeRequestsOptions{})
}
//MRFromArgsWithOpts gets MR with custom request options passed down to it
func MRFromArgsWithOpts(f *cmdutils.Factory, args []string, opts *gitlab.GetMergeRequestsOptions) (*gitlab.MergeRequest, glrepo.Interface, error) {
var mrID int
var mr *gitlab.MergeRequest
apiClient, err := f.HttpClient()
if err != nil {
return nil, nil, err
}
baseRepo, err := f.BaseRepo()
if err != nil {
return nil, nil, err
}
branch, err := f.Branch()
if err != nil {
return nil, nil, err
}
if len(args) > 0 {
mrID, err = strconv.Atoi(args[0])
if err != nil {
branch = args[0]
} else if mrID == 0 { // to check for cases where the user explicitly specified mrID to be zero
return nil, nil, fmt.Errorf("invalid merge request ID provided")
}
}
if mrID == 0 {
mr, err = GetOpenMRForBranch(apiClient, baseRepo, branch)
if err != nil {
return nil, nil, err
}
mrID = mr.IID
}
// fetching multiple MRs does not return many major params in the payload
// so we fetch again using the single mr endpoint
mr, err = api.GetMR(apiClient, baseRepo.FullName(), mrID, opts)
if err != nil {
return nil, nil, fmt.Errorf("failed to get merge request %d: %w", mrID, err)
}
return mr, baseRepo, nil
}
func MRsFromArgs(f *cmdutils.Factory, args []string) ([]*gitlab.MergeRequest, glrepo.Interface, error) {
if len(args) <= 1 {
var arrIDs []string
if len(args) == 1 {
arrIDs = strings.Split(args[0], ",")
}
if len(arrIDs) <= 1 {
mr, baseRepo, err := MRFromArgs(f, args)
if err != nil {
return nil, nil, err
}
return []*gitlab.MergeRequest{mr}, baseRepo, err
}
args = arrIDs
}
apiClient, err := f.HttpClient()
if err != nil {
return nil, nil, err
}
baseRepo, err := f.BaseRepo()
if err != nil {
return nil, nil, err
}
errGroup, _ := errgroup.WithContext(context.Background())
mrs := make([]*gitlab.MergeRequest, len(args))
for i, arg := range args {
i, arg := i, arg
errGroup.Go(func() error {
mrID, err := strconv.Atoi(arg)
if err != nil {
return err
}
if mrID == 0 {
return fmt.Errorf("invalid merge request ID provided")
}
// fetching multiple MRs does not return many major params in the payload
// so we fetch again using the single mr endpoint
mr, err := api.GetMR(apiClient, baseRepo.FullName(), mrID, &gitlab.GetMergeRequestsOptions{})
if err != nil {
return fmt.Errorf("failed to get merge request %d: %w", mrID, err)
}
mrs[i] = mr
return nil
})
}
if err := errGroup.Wait(); err != nil {
return nil, nil, err
}
return mrs, baseRepo, nil
}
func GetOpenMRForBranch(apiClient *gitlab.Client, baseRepo glrepo.Interface, currentBranch string) (*gitlab.MergeRequest, error) {
mrs, err := api.ListMRs(apiClient, baseRepo.FullName(), &gitlab.ListProjectMergeRequestsOptions{
SourceBranch: gitlab.String(currentBranch),
State: gitlab.String("opened"),
})
if err != nil {
return nil, fmt.Errorf("failed to get open merge request for %q: %w", currentBranch, err)
}
if len(mrs) == 0 {
return nil, fmt.Errorf("no open merge request available for %q", currentBranch)
}
// A single result is expected since gitlab does not allow multiple merge requests for a single source branch
return mrs[0], nil
}