@@ -23,18 +23,21 @@ const (
23
23
24
24
// ProtectedBranch struct
25
25
type ProtectedBranch struct {
26
- ID int64 `xorm:"pk autoincr"`
27
- RepoID int64 `xorm:"UNIQUE(s)"`
28
- BranchName string `xorm:"UNIQUE(s)"`
29
- CanPush bool `xorm:"NOT NULL DEFAULT false"`
30
- EnableWhitelist bool
31
- WhitelistUserIDs []int64 `xorm:"JSON TEXT"`
32
- WhitelistTeamIDs []int64 `xorm:"JSON TEXT"`
33
- EnableMergeWhitelist bool `xorm:"NOT NULL DEFAULT false"`
34
- MergeWhitelistUserIDs []int64 `xorm:"JSON TEXT"`
35
- MergeWhitelistTeamIDs []int64 `xorm:"JSON TEXT"`
36
- CreatedUnix util.TimeStamp `xorm:"created"`
37
- UpdatedUnix util.TimeStamp `xorm:"updated"`
26
+ ID int64 `xorm:"pk autoincr"`
27
+ RepoID int64 `xorm:"UNIQUE(s)"`
28
+ BranchName string `xorm:"UNIQUE(s)"`
29
+ CanPush bool `xorm:"NOT NULL DEFAULT false"`
30
+ EnableWhitelist bool
31
+ WhitelistUserIDs []int64 `xorm:"JSON TEXT"`
32
+ WhitelistTeamIDs []int64 `xorm:"JSON TEXT"`
33
+ EnableMergeWhitelist bool `xorm:"NOT NULL DEFAULT false"`
34
+ MergeWhitelistUserIDs []int64 `xorm:"JSON TEXT"`
35
+ MergeWhitelistTeamIDs []int64 `xorm:"JSON TEXT"`
36
+ ApprovalsWhitelistUserIDs []int64 `xorm:"JSON TEXT"`
37
+ ApprovalsWhitelistTeamIDs []int64 `xorm:"JSON TEXT"`
38
+ RequiredApprovals int64 `xorm:"NOT NULL DEFAULT 0"`
39
+ CreatedUnix util.TimeStamp `xorm:"created"`
40
+ UpdatedUnix util.TimeStamp `xorm:"updated"`
38
41
}
39
42
40
43
// IsProtected returns if the branch is protected
@@ -86,6 +89,41 @@ func (protectBranch *ProtectedBranch) CanUserMerge(userID int64) bool {
86
89
return in
87
90
}
88
91
92
+ // HasEnoughApprovals returns true if pr has enough granted approvals.
93
+ func (protectBranch * ProtectedBranch ) HasEnoughApprovals (pr * PullRequest ) bool {
94
+ if protectBranch .RequiredApprovals == 0 {
95
+ return true
96
+ }
97
+ return protectBranch .GetGrantedApprovalsCount (pr ) >= protectBranch .RequiredApprovals
98
+ }
99
+
100
+ // GetGrantedApprovalsCount returns the number of granted approvals for pr. A granted approval must be authored by a user in an approval whitelist.
101
+ func (protectBranch * ProtectedBranch ) GetGrantedApprovalsCount (pr * PullRequest ) int64 {
102
+ reviews , err := GetReviewersByPullID (pr .ID )
103
+ if err != nil {
104
+ log .Error (1 , "GetUniqueApprovalsByPullRequestID:" , err )
105
+ return 0
106
+ }
107
+ approvals := int64 (0 )
108
+ userIDs := make ([]int64 , 0 )
109
+ for _ , review := range reviews {
110
+ if review .Type != ReviewTypeApprove {
111
+ continue
112
+ }
113
+ if base .Int64sContains (protectBranch .ApprovalsWhitelistUserIDs , review .ID ) {
114
+ approvals ++
115
+ continue
116
+ }
117
+ userIDs = append (userIDs , review .ID )
118
+ }
119
+ approvalTeamCount , err := UsersInTeamsCount (userIDs , protectBranch .ApprovalsWhitelistTeamIDs )
120
+ if err != nil {
121
+ log .Error (1 , "UsersInTeamsCount:" , err )
122
+ return 0
123
+ }
124
+ return approvalTeamCount + approvals
125
+ }
126
+
89
127
// GetProtectedBranchByRepoID getting protected branch by repo ID
90
128
func GetProtectedBranchByRepoID (RepoID int64 ) ([]* ProtectedBranch , error ) {
91
129
protectedBranches := make ([]* ProtectedBranch , 0 )
@@ -118,40 +156,64 @@ func GetProtectedBranchByID(id int64) (*ProtectedBranch, error) {
118
156
return rel , nil
119
157
}
120
158
159
+ // WhitelistOptions represent all sorts of whitelists used for protected branches
160
+ type WhitelistOptions struct {
161
+ UserIDs []int64
162
+ TeamIDs []int64
163
+
164
+ MergeUserIDs []int64
165
+ MergeTeamIDs []int64
166
+
167
+ ApprovalsUserIDs []int64
168
+ ApprovalsTeamIDs []int64
169
+ }
170
+
121
171
// UpdateProtectBranch saves branch protection options of repository.
122
172
// If ID is 0, it creates a new record. Otherwise, updates existing record.
123
173
// This function also performs check if whitelist user and team's IDs have been changed
124
174
// to avoid unnecessary whitelist delete and regenerate.
125
- func UpdateProtectBranch (repo * Repository , protectBranch * ProtectedBranch , whitelistUserIDs , whitelistTeamIDs , mergeWhitelistUserIDs , mergeWhitelistTeamIDs [] int64 ) (err error ) {
175
+ func UpdateProtectBranch (repo * Repository , protectBranch * ProtectedBranch , opts WhitelistOptions ) (err error ) {
126
176
if err = repo .GetOwner (); err != nil {
127
177
return fmt .Errorf ("GetOwner: %v" , err )
128
178
}
129
179
130
- whitelist , err := updateUserWhitelist (repo , protectBranch .WhitelistUserIDs , whitelistUserIDs )
180
+ whitelist , err := updateUserWhitelist (repo , protectBranch .WhitelistUserIDs , opts . UserIDs )
131
181
if err != nil {
132
182
return err
133
183
}
134
184
protectBranch .WhitelistUserIDs = whitelist
135
185
136
- whitelist , err = updateUserWhitelist (repo , protectBranch .MergeWhitelistUserIDs , mergeWhitelistUserIDs )
186
+ whitelist , err = updateUserWhitelist (repo , protectBranch .MergeWhitelistUserIDs , opts . MergeUserIDs )
137
187
if err != nil {
138
188
return err
139
189
}
140
190
protectBranch .MergeWhitelistUserIDs = whitelist
141
191
192
+ whitelist , err = updateUserWhitelist (repo , protectBranch .ApprovalsWhitelistUserIDs , opts .ApprovalsUserIDs )
193
+ if err != nil {
194
+ return err
195
+ }
196
+ protectBranch .ApprovalsWhitelistUserIDs = whitelist
197
+
142
198
// if the repo is in an organization
143
- whitelist , err = updateTeamWhitelist (repo , protectBranch .WhitelistTeamIDs , whitelistTeamIDs )
199
+ whitelist , err = updateTeamWhitelist (repo , protectBranch .WhitelistTeamIDs , opts . TeamIDs )
144
200
if err != nil {
145
201
return err
146
202
}
147
203
protectBranch .WhitelistTeamIDs = whitelist
148
204
149
- whitelist , err = updateTeamWhitelist (repo , protectBranch .MergeWhitelistTeamIDs , mergeWhitelistTeamIDs )
205
+ whitelist , err = updateTeamWhitelist (repo , protectBranch .MergeWhitelistTeamIDs , opts . MergeTeamIDs )
150
206
if err != nil {
151
207
return err
152
208
}
153
209
protectBranch .MergeWhitelistTeamIDs = whitelist
154
210
211
+ whitelist , err = updateTeamWhitelist (repo , protectBranch .ApprovalsWhitelistTeamIDs , opts .ApprovalsTeamIDs )
212
+ if err != nil {
213
+ return err
214
+ }
215
+ protectBranch .ApprovalsWhitelistTeamIDs = whitelist
216
+
155
217
// Make sure protectBranch.ID is not 0 for whitelists
156
218
if protectBranch .ID == 0 {
157
219
if _ , err = x .Insert (protectBranch ); err != nil {
@@ -213,7 +275,7 @@ func (repo *Repository) IsProtectedBranchForPush(branchName string, doer *User)
213
275
}
214
276
215
277
// IsProtectedBranchForMerging checks if branch is protected for merging
216
- func (repo * Repository ) IsProtectedBranchForMerging (branchName string , doer * User ) (bool , error ) {
278
+ func (repo * Repository ) IsProtectedBranchForMerging (pr * PullRequest , branchName string , doer * User ) (bool , error ) {
217
279
if doer == nil {
218
280
return true , nil
219
281
}
@@ -227,7 +289,7 @@ func (repo *Repository) IsProtectedBranchForMerging(branchName string, doer *Use
227
289
if err != nil {
228
290
return true , err
229
291
} else if has {
230
- return ! protectedBranch .CanUserMerge (doer .ID ), nil
292
+ return ! protectedBranch .CanUserMerge (doer .ID ) || ! protectedBranch . HasEnoughApprovals ( pr ) , nil
231
293
}
232
294
233
295
return false , nil
@@ -270,14 +332,14 @@ func updateTeamWhitelist(repo *Repository, currentWhitelist, newWhitelist []int6
270
332
return currentWhitelist , nil
271
333
}
272
334
273
- teams , err := GetTeamsWithAccessToRepo (repo .OwnerID , repo .ID , AccessModeWrite )
335
+ teams , err := GetTeamsWithAccessToRepo (repo .OwnerID , repo .ID , AccessModeRead )
274
336
if err != nil {
275
337
return nil , fmt .Errorf ("GetTeamsWithAccessToRepo [org_id: %d, repo_id: %d]: %v" , repo .OwnerID , repo .ID , err )
276
338
}
277
339
278
340
whitelist = make ([]int64 , 0 , len (teams ))
279
341
for i := range teams {
280
- if teams [ i ]. HasWriteAccess () && com .IsSliceContainsInt64 (newWhitelist , teams [i ].ID ) {
342
+ if com .IsSliceContainsInt64 (newWhitelist , teams [i ].ID ) {
281
343
whitelist = append (whitelist , teams [i ].ID )
282
344
}
283
345
}
0 commit comments