@@ -23,18 +23,21 @@ const (
2323
2424// ProtectedBranch struct
2525type 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"`
3841}
3942
4043// IsProtected returns if the branch is protected
@@ -86,6 +89,41 @@ func (protectBranch *ProtectedBranch) CanUserMerge(userID int64) bool {
8689 return in
8790}
8891
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+
89127// GetProtectedBranchByRepoID getting protected branch by repo ID
90128func GetProtectedBranchByRepoID (RepoID int64 ) ([]* ProtectedBranch , error ) {
91129 protectedBranches := make ([]* ProtectedBranch , 0 )
@@ -118,40 +156,64 @@ func GetProtectedBranchByID(id int64) (*ProtectedBranch, error) {
118156 return rel , nil
119157}
120158
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+
121171// UpdateProtectBranch saves branch protection options of repository.
122172// If ID is 0, it creates a new record. Otherwise, updates existing record.
123173// This function also performs check if whitelist user and team's IDs have been changed
124174// 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 ) {
126176 if err = repo .GetOwner (); err != nil {
127177 return fmt .Errorf ("GetOwner: %v" , err )
128178 }
129179
130- whitelist , err := updateUserWhitelist (repo , protectBranch .WhitelistUserIDs , whitelistUserIDs )
180+ whitelist , err := updateUserWhitelist (repo , protectBranch .WhitelistUserIDs , opts . UserIDs )
131181 if err != nil {
132182 return err
133183 }
134184 protectBranch .WhitelistUserIDs = whitelist
135185
136- whitelist , err = updateUserWhitelist (repo , protectBranch .MergeWhitelistUserIDs , mergeWhitelistUserIDs )
186+ whitelist , err = updateUserWhitelist (repo , protectBranch .MergeWhitelistUserIDs , opts . MergeUserIDs )
137187 if err != nil {
138188 return err
139189 }
140190 protectBranch .MergeWhitelistUserIDs = whitelist
141191
192+ whitelist , err = updateUserWhitelist (repo , protectBranch .ApprovalsWhitelistUserIDs , opts .ApprovalsUserIDs )
193+ if err != nil {
194+ return err
195+ }
196+ protectBranch .ApprovalsWhitelistUserIDs = whitelist
197+
142198 // if the repo is in an organization
143- whitelist , err = updateTeamWhitelist (repo , protectBranch .WhitelistTeamIDs , whitelistTeamIDs )
199+ whitelist , err = updateTeamWhitelist (repo , protectBranch .WhitelistTeamIDs , opts . TeamIDs )
144200 if err != nil {
145201 return err
146202 }
147203 protectBranch .WhitelistTeamIDs = whitelist
148204
149- whitelist , err = updateTeamWhitelist (repo , protectBranch .MergeWhitelistTeamIDs , mergeWhitelistTeamIDs )
205+ whitelist , err = updateTeamWhitelist (repo , protectBranch .MergeWhitelistTeamIDs , opts . MergeTeamIDs )
150206 if err != nil {
151207 return err
152208 }
153209 protectBranch .MergeWhitelistTeamIDs = whitelist
154210
211+ whitelist , err = updateTeamWhitelist (repo , protectBranch .ApprovalsWhitelistTeamIDs , opts .ApprovalsTeamIDs )
212+ if err != nil {
213+ return err
214+ }
215+ protectBranch .ApprovalsWhitelistTeamIDs = whitelist
216+
155217 // Make sure protectBranch.ID is not 0 for whitelists
156218 if protectBranch .ID == 0 {
157219 if _ , err = x .Insert (protectBranch ); err != nil {
@@ -213,7 +275,7 @@ func (repo *Repository) IsProtectedBranchForPush(branchName string, doer *User)
213275}
214276
215277// 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 ) {
217279 if doer == nil {
218280 return true , nil
219281 }
@@ -227,7 +289,7 @@ func (repo *Repository) IsProtectedBranchForMerging(branchName string, doer *Use
227289 if err != nil {
228290 return true , err
229291 } else if has {
230- return ! protectedBranch .CanUserMerge (doer .ID ), nil
292+ return ! protectedBranch .CanUserMerge (doer .ID ) || ! protectedBranch . HasEnoughApprovals ( pr ) , nil
231293 }
232294
233295 return false , nil
@@ -270,14 +332,14 @@ func updateTeamWhitelist(repo *Repository, currentWhitelist, newWhitelist []int6
270332 return currentWhitelist , nil
271333 }
272334
273- teams , err := GetTeamsWithAccessToRepo (repo .OwnerID , repo .ID , AccessModeWrite )
335+ teams , err := GetTeamsWithAccessToRepo (repo .OwnerID , repo .ID , AccessModeRead )
274336 if err != nil {
275337 return nil , fmt .Errorf ("GetTeamsWithAccessToRepo [org_id: %d, repo_id: %d]: %v" , repo .OwnerID , repo .ID , err )
276338 }
277339
278340 whitelist = make ([]int64 , 0 , len (teams ))
279341 for i := range teams {
280- if teams [ i ]. HasWriteAccess () && com .IsSliceContainsInt64 (newWhitelist , teams [i ].ID ) {
342+ if com .IsSliceContainsInt64 (newWhitelist , teams [i ].ID ) {
281343 whitelist = append (whitelist , teams [i ].ID )
282344 }
283345 }
0 commit comments