2017-02-21 18:02:10 +03:00
// Copyright 2016 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
import (
2019-12-15 12:51:28 +03:00
"context"
2017-02-21 18:02:10 +03:00
"fmt"
2020-03-27 01:26:34 +03:00
"strings"
2017-02-21 18:02:10 +03:00
"time"
2017-09-14 11:16:22 +03:00
2021-09-19 14:49:59 +03:00
"code.gitea.io/gitea/models/db"
2022-03-29 09:29:02 +03:00
"code.gitea.io/gitea/models/organization"
2021-11-28 14:58:28 +03:00
"code.gitea.io/gitea/models/perm"
2022-05-11 13:09:36 +03:00
access_model "code.gitea.io/gitea/models/perm/access"
2021-12-10 04:27:50 +03:00
repo_model "code.gitea.io/gitea/models/repo"
2021-11-09 22:57:58 +03:00
"code.gitea.io/gitea/models/unit"
2021-11-24 12:49:20 +03:00
user_model "code.gitea.io/gitea/models/user"
2017-09-14 11:16:22 +03:00
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/log"
2019-08-15 17:46:21 +03:00
"code.gitea.io/gitea/modules/timeutil"
2017-09-14 11:16:22 +03:00
"code.gitea.io/gitea/modules/util"
2020-03-27 01:26:34 +03:00
"github.com/gobwas/glob"
2017-02-21 18:02:10 +03:00
)
// ProtectedBranch struct
type ProtectedBranch struct {
2020-11-28 22:30:46 +03:00
ID int64 ` xorm:"pk autoincr" `
RepoID int64 ` xorm:"UNIQUE(s)" `
BranchName string ` xorm:"UNIQUE(s)" `
CanPush bool ` xorm:"NOT NULL DEFAULT false" `
EnableWhitelist bool
WhitelistUserIDs [ ] int64 ` xorm:"JSON TEXT" `
WhitelistTeamIDs [ ] int64 ` xorm:"JSON TEXT" `
EnableMergeWhitelist bool ` xorm:"NOT NULL DEFAULT false" `
WhitelistDeployKeys bool ` xorm:"NOT NULL DEFAULT false" `
MergeWhitelistUserIDs [ ] int64 ` xorm:"JSON TEXT" `
MergeWhitelistTeamIDs [ ] int64 ` xorm:"JSON TEXT" `
EnableStatusCheck bool ` xorm:"NOT NULL DEFAULT false" `
StatusCheckContexts [ ] string ` xorm:"JSON TEXT" `
EnableApprovalsWhitelist bool ` xorm:"NOT NULL DEFAULT false" `
ApprovalsWhitelistUserIDs [ ] int64 ` xorm:"JSON TEXT" `
ApprovalsWhitelistTeamIDs [ ] int64 ` xorm:"JSON TEXT" `
RequiredApprovals int64 ` xorm:"NOT NULL DEFAULT 0" `
BlockOnRejectedReviews bool ` xorm:"NOT NULL DEFAULT false" `
BlockOnOfficialReviewRequests bool ` xorm:"NOT NULL DEFAULT false" `
BlockOnOutdatedBranch bool ` xorm:"NOT NULL DEFAULT false" `
DismissStaleApprovals bool ` xorm:"NOT NULL DEFAULT false" `
RequireSignedCommits bool ` xorm:"NOT NULL DEFAULT false" `
ProtectedFilePatterns string ` xorm:"TEXT" `
2021-09-11 17:21:17 +03:00
UnprotectedFilePatterns string ` xorm:"TEXT" `
2020-01-09 04:47:45 +03:00
CreatedUnix timeutil . TimeStamp ` xorm:"created" `
UpdatedUnix timeutil . TimeStamp ` xorm:"updated" `
2017-09-14 11:16:22 +03:00
}
2021-09-19 14:49:59 +03:00
func init ( ) {
db . RegisterModel ( new ( ProtectedBranch ) )
db . RegisterModel ( new ( DeletedBranch ) )
2021-10-08 20:03:04 +03:00
db . RegisterModel ( new ( RenamedBranch ) )
2021-09-19 14:49:59 +03:00
}
2017-09-14 11:16:22 +03:00
// IsProtected returns if the branch is protected
func ( protectBranch * ProtectedBranch ) IsProtected ( ) bool {
return protectBranch . ID > 0
}
// CanUserPush returns if some user could push to this protected branch
func ( protectBranch * ProtectedBranch ) CanUserPush ( userID int64 ) bool {
2019-12-04 04:08:56 +03:00
if ! protectBranch . CanPush {
2017-09-14 11:16:22 +03:00
return false
}
2019-12-04 04:08:56 +03:00
if ! protectBranch . EnableWhitelist {
2021-11-24 12:49:20 +03:00
if user , err := user_model . GetUserByID ( userID ) ; err != nil {
2019-12-04 04:08:56 +03:00
log . Error ( "GetUserByID: %v" , err )
return false
2021-12-10 04:27:50 +03:00
} else if repo , err := repo_model . GetRepositoryByID ( protectBranch . RepoID ) ; err != nil {
log . Error ( "repo_model.GetRepositoryByID: %v" , err )
2019-12-04 04:08:56 +03:00
return false
2022-05-11 13:09:36 +03:00
} else if writeAccess , err := access_model . HasAccessUnit ( db . DefaultContext , user , repo , unit . TypeCode , perm . AccessModeWrite ) ; err != nil {
2019-12-04 04:08:56 +03:00
log . Error ( "HasAccessUnit: %v" , err )
return false
} else {
return writeAccess
}
}
2017-09-14 11:16:22 +03:00
if base . Int64sContains ( protectBranch . WhitelistUserIDs , userID ) {
return true
}
if len ( protectBranch . WhitelistTeamIDs ) == 0 {
return false
}
2022-03-29 09:29:02 +03:00
in , err := organization . IsUserInTeams ( db . DefaultContext , userID , protectBranch . WhitelistTeamIDs )
2017-09-14 11:16:22 +03:00
if err != nil {
2019-04-02 10:48:31 +03:00
log . Error ( "IsUserInTeams: %v" , err )
2017-09-14 11:16:22 +03:00
return false
}
return in
2017-02-21 18:02:10 +03:00
}
2020-01-11 10:29:34 +03:00
// IsUserMergeWhitelisted checks if some user is whitelisted to merge to this branch
2022-05-11 13:09:36 +03:00
func IsUserMergeWhitelisted ( ctx context . Context , protectBranch * ProtectedBranch , userID int64 , permissionInRepo access_model . Permission ) bool {
2018-03-25 13:01:32 +03:00
if ! protectBranch . EnableMergeWhitelist {
2020-08-20 10:48:40 +03:00
// Then we need to fall back on whether the user has write permission
2021-11-09 22:57:58 +03:00
return permissionInRepo . CanWrite ( unit . TypeCode )
2018-03-25 13:01:32 +03:00
}
if base . Int64sContains ( protectBranch . MergeWhitelistUserIDs , userID ) {
return true
}
2018-07-27 22:11:24 +03:00
if len ( protectBranch . MergeWhitelistTeamIDs ) == 0 {
2018-03-25 13:01:32 +03:00
return false
}
2022-05-03 22:46:28 +03:00
in , err := organization . IsUserInTeams ( ctx , userID , protectBranch . MergeWhitelistTeamIDs )
2018-03-25 13:01:32 +03:00
if err != nil {
2019-04-02 10:48:31 +03:00
log . Error ( "IsUserInTeams: %v" , err )
2018-03-25 13:01:32 +03:00
return false
}
return in
}
2019-12-04 04:08:56 +03:00
// IsUserOfficialReviewer check if user is official reviewer for the branch (counts towards required approvals)
2021-12-10 04:27:50 +03:00
func IsUserOfficialReviewer ( protectBranch * ProtectedBranch , user * user_model . User ) ( bool , error ) {
return isUserOfficialReviewer ( db . DefaultContext , protectBranch , user )
2019-12-04 04:08:56 +03:00
}
2021-12-10 04:27:50 +03:00
func isUserOfficialReviewer ( ctx context . Context , protectBranch * ProtectedBranch , user * user_model . User ) ( bool , error ) {
repo , err := repo_model . GetRepositoryByIDCtx ( ctx , protectBranch . RepoID )
2019-12-04 04:08:56 +03:00
if err != nil {
return false , err
}
if ! protectBranch . EnableApprovalsWhitelist {
// Anyone with write access is considered official reviewer
2022-05-11 13:09:36 +03:00
writeAccess , err := access_model . HasAccessUnit ( ctx , user , repo , unit . TypeCode , perm . AccessModeWrite )
2019-12-04 04:08:56 +03:00
if err != nil {
return false , err
}
return writeAccess , nil
}
if base . Int64sContains ( protectBranch . ApprovalsWhitelistUserIDs , user . ID ) {
return true , nil
}
2022-03-29 09:29:02 +03:00
inTeam , err := organization . IsUserInTeams ( ctx , user . ID , protectBranch . ApprovalsWhitelistTeamIDs )
2019-12-04 04:08:56 +03:00
if err != nil {
return false , err
}
return inTeam , nil
}
2018-12-11 14:28:37 +03:00
// HasEnoughApprovals returns true if pr has enough granted approvals.
2022-05-03 22:46:28 +03:00
func ( protectBranch * ProtectedBranch ) HasEnoughApprovals ( ctx context . Context , pr * PullRequest ) bool {
2018-12-11 14:28:37 +03:00
if protectBranch . RequiredApprovals == 0 {
return true
}
2022-05-03 22:46:28 +03:00
return protectBranch . GetGrantedApprovalsCount ( ctx , pr ) >= protectBranch . RequiredApprovals
2018-12-11 14:28:37 +03:00
}
// GetGrantedApprovalsCount returns the number of granted approvals for pr. A granted approval must be authored by a user in an approval whitelist.
2022-05-03 22:46:28 +03:00
func ( protectBranch * ProtectedBranch ) GetGrantedApprovalsCount ( ctx context . Context , pr * PullRequest ) int64 {
sess := db . GetEngine ( ctx ) . Where ( "issue_id = ?" , pr . IssueID ) .
2019-12-04 04:08:56 +03:00
And ( "type = ?" , ReviewTypeApprove ) .
2021-02-11 20:32:25 +03:00
And ( "official = ?" , true ) .
And ( "dismissed = ?" , false )
2020-01-09 04:47:45 +03:00
if protectBranch . DismissStaleApprovals {
sess = sess . And ( "stale = ?" , false )
}
approvals , err := sess . Count ( new ( Review ) )
2018-12-11 14:28:37 +03:00
if err != nil {
2019-12-04 04:08:56 +03:00
log . Error ( "GetGrantedApprovalsCount: %v" , err )
2018-12-11 14:28:37 +03:00
return 0
}
2018-12-12 02:49:33 +03:00
2019-12-04 04:08:56 +03:00
return approvals
2018-12-11 14:28:37 +03:00
}
2020-01-03 20:47:10 +03:00
// MergeBlockedByRejectedReview returns true if merge is blocked by rejected reviews
2022-05-03 22:46:28 +03:00
func ( protectBranch * ProtectedBranch ) MergeBlockedByRejectedReview ( ctx context . Context , pr * PullRequest ) bool {
2020-01-03 20:47:10 +03:00
if ! protectBranch . BlockOnRejectedReviews {
return false
}
2022-05-03 22:46:28 +03:00
rejectExist , err := db . GetEngine ( ctx ) . Where ( "issue_id = ?" , pr . IssueID ) .
2020-11-28 22:30:46 +03:00
And ( "type = ?" , ReviewTypeReject ) .
2020-01-03 20:47:10 +03:00
And ( "official = ?" , true ) .
2021-02-11 20:32:25 +03:00
And ( "dismissed = ?" , false ) .
2020-01-03 20:47:10 +03:00
Exist ( new ( Review ) )
if err != nil {
log . Error ( "MergeBlockedByRejectedReview: %v" , err )
return true
}
return rejectExist
}
2020-11-28 22:30:46 +03:00
// MergeBlockedByOfficialReviewRequests block merge because of some review request to official reviewer
// of from official review
2022-05-03 22:46:28 +03:00
func ( protectBranch * ProtectedBranch ) MergeBlockedByOfficialReviewRequests ( ctx context . Context , pr * PullRequest ) bool {
2020-11-28 22:30:46 +03:00
if ! protectBranch . BlockOnOfficialReviewRequests {
return false
}
2022-05-03 22:46:28 +03:00
has , err := db . GetEngine ( ctx ) . Where ( "issue_id = ?" , pr . IssueID ) .
2020-11-28 22:30:46 +03:00
And ( "type = ?" , ReviewTypeRequest ) .
And ( "official = ?" , true ) .
Exist ( new ( Review ) )
if err != nil {
log . Error ( "MergeBlockedByOfficialReviewRequests: %v" , err )
return true
}
return has
}
2020-04-17 04:00:36 +03:00
// MergeBlockedByOutdatedBranch returns true if merge is blocked by an outdated head branch
func ( protectBranch * ProtectedBranch ) MergeBlockedByOutdatedBranch ( pr * PullRequest ) bool {
return protectBranch . BlockOnOutdatedBranch && pr . CommitsBehind > 0
}
2020-03-27 01:26:34 +03:00
// GetProtectedFilePatterns parses a semicolon separated list of protected file patterns and returns a glob.Glob slice
func ( protectBranch * ProtectedBranch ) GetProtectedFilePatterns ( ) [ ] glob . Glob {
2021-09-11 17:21:17 +03:00
return getFilePatterns ( protectBranch . ProtectedFilePatterns )
}
// GetUnprotectedFilePatterns parses a semicolon separated list of unprotected file patterns and returns a glob.Glob slice
func ( protectBranch * ProtectedBranch ) GetUnprotectedFilePatterns ( ) [ ] glob . Glob {
return getFilePatterns ( protectBranch . UnprotectedFilePatterns )
}
func getFilePatterns ( filePatterns string ) [ ] glob . Glob {
2020-03-27 01:26:34 +03:00
extarr := make ( [ ] glob . Glob , 0 , 10 )
2021-09-11 17:21:17 +03:00
for _ , expr := range strings . Split ( strings . ToLower ( filePatterns ) , ";" ) {
2020-03-27 01:26:34 +03:00
expr = strings . TrimSpace ( expr )
if expr != "" {
if g , err := glob . Compile ( expr , '.' , '/' ) ; err != nil {
2021-07-08 14:38:13 +03:00
log . Info ( "Invalid glob expression '%s' (skipped): %v" , expr , err )
2020-03-27 01:26:34 +03:00
} else {
extarr = append ( extarr , g )
}
}
}
return extarr
}
2020-10-13 21:50:57 +03:00
// MergeBlockedByProtectedFiles returns true if merge is blocked by protected files change
func ( protectBranch * ProtectedBranch ) MergeBlockedByProtectedFiles ( pr * PullRequest ) bool {
glob := protectBranch . GetProtectedFilePatterns ( )
if len ( glob ) == 0 {
return false
}
return len ( pr . ChangedProtectedFiles ) > 0
}
// IsProtectedFile return if path is protected
func ( protectBranch * ProtectedBranch ) IsProtectedFile ( patterns [ ] glob . Glob , path string ) bool {
if len ( patterns ) == 0 {
patterns = protectBranch . GetProtectedFilePatterns ( )
if len ( patterns ) == 0 {
return false
}
}
lpath := strings . ToLower ( strings . TrimSpace ( path ) )
r := false
for _ , pat := range patterns {
if pat . Match ( lpath ) {
r = true
break
}
}
return r
}
2021-09-11 17:21:17 +03:00
// IsUnprotectedFile return if path is unprotected
func ( protectBranch * ProtectedBranch ) IsUnprotectedFile ( patterns [ ] glob . Glob , path string ) bool {
if len ( patterns ) == 0 {
patterns = protectBranch . GetUnprotectedFilePatterns ( )
if len ( patterns ) == 0 {
return false
}
}
lpath := strings . ToLower ( strings . TrimSpace ( path ) )
r := false
for _ , pat := range patterns {
if pat . Match ( lpath ) {
r = true
break
}
}
return r
}
2017-02-21 18:02:10 +03:00
// GetProtectedBranchBy getting protected branch by ID/Name
2022-05-20 17:08:52 +03:00
func GetProtectedBranchBy ( ctx context . Context , repoID int64 , branchName string ) ( * ProtectedBranch , error ) {
2019-06-12 22:41:28 +03:00
rel := & ProtectedBranch { RepoID : repoID , BranchName : branchName }
2022-05-20 17:08:52 +03:00
has , err := db . GetByBean ( ctx , rel )
2017-02-21 18:02:10 +03:00
if err != nil {
return nil , err
}
if ! has {
return nil , nil
}
return rel , nil
}
2018-12-11 14:28:37 +03:00
// WhitelistOptions represent all sorts of whitelists used for protected branches
type WhitelistOptions struct {
UserIDs [ ] int64
TeamIDs [ ] int64
MergeUserIDs [ ] int64
MergeTeamIDs [ ] int64
ApprovalsUserIDs [ ] int64
ApprovalsTeamIDs [ ] int64
}
2017-09-14 11:16:22 +03:00
// UpdateProtectBranch saves branch protection options of repository.
// If ID is 0, it creates a new record. Otherwise, updates existing record.
// This function also performs check if whitelist user and team's IDs have been changed
// to avoid unnecessary whitelist delete and regenerate.
2022-04-28 14:48:48 +03:00
func UpdateProtectBranch ( ctx context . Context , repo * repo_model . Repository , protectBranch * ProtectedBranch , opts WhitelistOptions ) ( err error ) {
if err = repo . GetOwner ( ctx ) ; err != nil {
2017-09-14 11:16:22 +03:00
return fmt . Errorf ( "GetOwner: %v" , err )
}
2022-04-28 14:48:48 +03:00
whitelist , err := updateUserWhitelist ( ctx , repo , protectBranch . WhitelistUserIDs , opts . UserIDs )
2018-03-25 13:01:32 +03:00
if err != nil {
return err
}
protectBranch . WhitelistUserIDs = whitelist
2017-09-14 11:16:22 +03:00
2022-04-28 14:48:48 +03:00
whitelist , err = updateUserWhitelist ( ctx , repo , protectBranch . MergeWhitelistUserIDs , opts . MergeUserIDs )
2018-03-25 13:01:32 +03:00
if err != nil {
return err
2017-09-14 11:16:22 +03:00
}
2018-03-25 13:01:32 +03:00
protectBranch . MergeWhitelistUserIDs = whitelist
2017-09-14 11:16:22 +03:00
2022-04-28 14:48:48 +03:00
whitelist , err = updateApprovalWhitelist ( ctx , repo , protectBranch . ApprovalsWhitelistUserIDs , opts . ApprovalsUserIDs )
2018-12-11 14:28:37 +03:00
if err != nil {
return err
}
protectBranch . ApprovalsWhitelistUserIDs = whitelist
2018-03-25 13:01:32 +03:00
// if the repo is in an organization
2022-04-28 14:48:48 +03:00
whitelist , err = updateTeamWhitelist ( ctx , repo , protectBranch . WhitelistTeamIDs , opts . TeamIDs )
2018-03-25 13:01:32 +03:00
if err != nil {
return err
2017-09-14 11:16:22 +03:00
}
2018-03-25 13:01:32 +03:00
protectBranch . WhitelistTeamIDs = whitelist
2022-04-28 14:48:48 +03:00
whitelist , err = updateTeamWhitelist ( ctx , repo , protectBranch . MergeWhitelistTeamIDs , opts . MergeTeamIDs )
2018-03-25 13:01:32 +03:00
if err != nil {
return err
}
protectBranch . MergeWhitelistTeamIDs = whitelist
2017-09-14 11:16:22 +03:00
2022-04-28 14:48:48 +03:00
whitelist , err = updateTeamWhitelist ( ctx , repo , protectBranch . ApprovalsWhitelistTeamIDs , opts . ApprovalsTeamIDs )
2018-12-11 14:28:37 +03:00
if err != nil {
return err
}
protectBranch . ApprovalsWhitelistTeamIDs = whitelist
2017-09-14 11:16:22 +03:00
// Make sure protectBranch.ID is not 0 for whitelists
if protectBranch . ID == 0 {
2022-04-28 14:48:48 +03:00
if _ , err = db . GetEngine ( ctx ) . Insert ( protectBranch ) ; err != nil {
2017-09-14 11:16:22 +03:00
return fmt . Errorf ( "Insert: %v" , err )
}
return nil
}
2022-04-28 14:48:48 +03:00
if _ , err = db . GetEngine ( ctx ) . ID ( protectBranch . ID ) . AllCols ( ) . Update ( protectBranch ) ; err != nil {
2017-09-14 11:16:22 +03:00
return fmt . Errorf ( "Update: %v" , err )
}
return nil
}
2017-03-15 03:52:01 +03:00
// GetProtectedBranches get all protected branches
2021-12-10 04:27:50 +03:00
func GetProtectedBranches ( repoID int64 ) ( [ ] * ProtectedBranch , error ) {
2017-02-21 18:02:10 +03:00
protectedBranches := make ( [ ] * ProtectedBranch , 0 )
2021-12-10 04:27:50 +03:00
return protectedBranches , db . GetEngine ( db . DefaultContext ) . Find ( & protectedBranches , & ProtectedBranch { RepoID : repoID } )
2019-11-16 22:39:18 +03:00
}
2017-05-02 03:49:55 +03:00
// IsProtectedBranch checks if branch is protected
2021-12-10 04:27:50 +03:00
func IsProtectedBranch ( repoID int64 , branchName string ) ( bool , error ) {
2017-05-02 03:49:55 +03:00
protectedBranch := & ProtectedBranch {
2021-12-10 04:27:50 +03:00
RepoID : repoID ,
2017-05-02 03:49:55 +03:00
BranchName : branchName ,
}
2021-09-23 18:45:36 +03:00
has , err := db . GetEngine ( db . DefaultContext ) . Exist ( protectedBranch )
2018-08-08 06:17:11 +03:00
if err != nil {
return true , err
}
return has , nil
}
2019-10-08 22:18:17 +03:00
// updateApprovalWhitelist checks whether the user whitelist changed and returns a whitelist with
// the users from newWhitelist which have explicit read or write access to the repo.
2022-04-28 14:48:48 +03:00
func updateApprovalWhitelist ( ctx context . Context , repo * repo_model . Repository , currentWhitelist , newWhitelist [ ] int64 ) ( whitelist [ ] int64 , err error ) {
2019-10-08 22:18:17 +03:00
hasUsersChanged := ! util . IsSliceInt64Eq ( currentWhitelist , newWhitelist )
if ! hasUsersChanged {
return currentWhitelist , nil
}
whitelist = make ( [ ] int64 , 0 , len ( newWhitelist ) )
for _ , userID := range newWhitelist {
2022-05-11 13:09:36 +03:00
if reader , err := access_model . IsRepoReader ( ctx , repo , userID ) ; err != nil {
2019-10-08 22:18:17 +03:00
return nil , err
} else if ! reader {
continue
}
whitelist = append ( whitelist , userID )
}
return
}
2018-03-25 13:01:32 +03:00
// updateUserWhitelist checks whether the user whitelist changed and returns a whitelist with
// the users from newWhitelist which have write access to the repo.
2022-04-28 14:48:48 +03:00
func updateUserWhitelist ( ctx context . Context , repo * repo_model . Repository , currentWhitelist , newWhitelist [ ] int64 ) ( whitelist [ ] int64 , err error ) {
2018-03-25 13:01:32 +03:00
hasUsersChanged := ! util . IsSliceInt64Eq ( currentWhitelist , newWhitelist )
if ! hasUsersChanged {
return currentWhitelist , nil
}
whitelist = make ( [ ] int64 , 0 , len ( newWhitelist ) )
for _ , userID := range newWhitelist {
2022-04-28 14:48:48 +03:00
user , err := user_model . GetUserByIDCtx ( ctx , userID )
2018-03-25 13:01:32 +03:00
if err != nil {
2018-11-28 14:26:14 +03:00
return nil , fmt . Errorf ( "GetUserByID [user_id: %d, repo_id: %d]: %v" , userID , repo . ID , err )
}
2022-05-11 13:09:36 +03:00
perm , err := access_model . GetUserRepoPermission ( ctx , repo , user )
2018-11-28 14:26:14 +03:00
if err != nil {
return nil , fmt . Errorf ( "GetUserRepoPermission [user_id: %d, repo_id: %d]: %v" , userID , repo . ID , err )
}
2021-11-09 22:57:58 +03:00
if ! perm . CanWrite ( unit . TypeCode ) {
2018-03-25 13:01:32 +03:00
continue // Drop invalid user ID
}
whitelist = append ( whitelist , userID )
}
return
}
// updateTeamWhitelist checks whether the team whitelist changed and returns a whitelist with
// the teams from newWhitelist which have write access to the repo.
2022-04-28 14:48:48 +03:00
func updateTeamWhitelist ( ctx context . Context , repo * repo_model . Repository , currentWhitelist , newWhitelist [ ] int64 ) ( whitelist [ ] int64 , err error ) {
2018-03-25 13:01:32 +03:00
hasTeamsChanged := ! util . IsSliceInt64Eq ( currentWhitelist , newWhitelist )
if ! hasTeamsChanged {
return currentWhitelist , nil
}
2022-04-28 14:48:48 +03:00
teams , err := organization . GetTeamsWithAccessToRepo ( ctx , repo . OwnerID , repo . ID , perm . AccessModeRead )
2018-03-25 13:01:32 +03:00
if err != nil {
return nil , fmt . Errorf ( "GetTeamsWithAccessToRepo [org_id: %d, repo_id: %d]: %v" , repo . OwnerID , repo . ID , err )
}
whitelist = make ( [ ] int64 , 0 , len ( teams ) )
for i := range teams {
2020-12-25 12:59:32 +03:00
if util . IsInt64InSlice ( teams [ i ] . ID , newWhitelist ) {
2018-03-25 13:01:32 +03:00
whitelist = append ( whitelist , teams [ i ] . ID )
}
}
return
}
2017-02-21 18:02:10 +03:00
// DeleteProtectedBranch removes ProtectedBranch relation between the user and repository.
2021-12-10 04:27:50 +03:00
func DeleteProtectedBranch ( repoID , id int64 ) ( err error ) {
2017-02-21 18:02:10 +03:00
protectedBranch := & ProtectedBranch {
2021-12-10 04:27:50 +03:00
RepoID : repoID ,
2017-02-21 18:02:10 +03:00
ID : id ,
}
2021-09-23 18:45:36 +03:00
if affected , err := db . GetEngine ( db . DefaultContext ) . Delete ( protectedBranch ) ; err != nil {
2017-02-21 18:02:10 +03:00
return err
} else if affected != 1 {
return fmt . Errorf ( "delete protected branch ID(%v) failed" , id )
}
2021-09-19 14:49:59 +03:00
return nil
2017-02-21 18:02:10 +03:00
}
2017-10-26 03:49:16 +03:00
// DeletedBranch struct
type DeletedBranch struct {
2019-08-15 17:46:21 +03:00
ID int64 ` xorm:"pk autoincr" `
RepoID int64 ` xorm:"UNIQUE(s) INDEX NOT NULL" `
Name string ` xorm:"UNIQUE(s) NOT NULL" `
Commit string ` xorm:"UNIQUE(s) NOT NULL" `
DeletedByID int64 ` xorm:"INDEX" `
2021-11-24 12:49:20 +03:00
DeletedBy * user_model . User ` xorm:"-" `
2019-08-15 17:46:21 +03:00
DeletedUnix timeutil . TimeStamp ` xorm:"INDEX created" `
2017-10-26 03:49:16 +03:00
}
// AddDeletedBranch adds a deleted branch to the database
2021-12-10 04:27:50 +03:00
func AddDeletedBranch ( repoID int64 , branchName , commit string , deletedByID int64 ) error {
2017-10-26 03:49:16 +03:00
deletedBranch := & DeletedBranch {
2021-12-10 04:27:50 +03:00
RepoID : repoID ,
2017-10-26 03:49:16 +03:00
Name : branchName ,
Commit : commit ,
DeletedByID : deletedByID ,
}
2021-12-10 04:27:50 +03:00
_ , err := db . GetEngine ( db . DefaultContext ) . Insert ( deletedBranch )
2021-09-19 14:49:59 +03:00
return err
2017-10-26 03:49:16 +03:00
}
// GetDeletedBranches returns all the deleted branches
2021-12-10 04:27:50 +03:00
func GetDeletedBranches ( repoID int64 ) ( [ ] * DeletedBranch , error ) {
2017-10-26 03:49:16 +03:00
deletedBranches := make ( [ ] * DeletedBranch , 0 )
2021-12-10 04:27:50 +03:00
return deletedBranches , db . GetEngine ( db . DefaultContext ) . Where ( "repo_id = ?" , repoID ) . Desc ( "deleted_unix" ) . Find ( & deletedBranches )
2017-10-26 03:49:16 +03:00
}
// GetDeletedBranchByID get a deleted branch by its ID
2021-12-10 04:27:50 +03:00
func GetDeletedBranchByID ( repoID , id int64 ) ( * DeletedBranch , error ) {
2020-06-17 20:50:11 +03:00
deletedBranch := & DeletedBranch { }
2021-12-10 04:27:50 +03:00
has , err := db . GetEngine ( db . DefaultContext ) . Where ( "repo_id = ?" , repoID ) . And ( "id = ?" , id ) . Get ( deletedBranch )
2017-10-26 03:49:16 +03:00
if err != nil {
return nil , err
}
if ! has {
return nil , nil
}
return deletedBranch , nil
}
2021-12-10 04:27:50 +03:00
// RemoveDeletedBranchByID removes a deleted branch from the database
func RemoveDeletedBranchByID ( repoID , id int64 ) ( err error ) {
2017-10-26 03:49:16 +03:00
deletedBranch := & DeletedBranch {
2021-12-10 04:27:50 +03:00
RepoID : repoID ,
2017-10-26 03:49:16 +03:00
ID : id ,
}
2021-09-23 18:45:36 +03:00
if affected , err := db . GetEngine ( db . DefaultContext ) . Delete ( deletedBranch ) ; err != nil {
2017-10-26 03:49:16 +03:00
return err
} else if affected != 1 {
return fmt . Errorf ( "remove deleted branch ID(%v) failed" , id )
}
2021-09-19 14:49:59 +03:00
return nil
2017-10-26 03:49:16 +03:00
}
// LoadUser loads the user that deleted the branch
2021-11-24 12:49:20 +03:00
// When there's no user found it returns a user_model.NewGhostUser
2017-10-26 03:49:16 +03:00
func ( deletedBranch * DeletedBranch ) LoadUser ( ) {
2021-11-24 12:49:20 +03:00
user , err := user_model . GetUserByID ( deletedBranch . DeletedByID )
2017-10-26 03:49:16 +03:00
if err != nil {
2021-11-24 12:49:20 +03:00
user = user_model . NewGhostUser ( )
2017-10-26 03:49:16 +03:00
}
deletedBranch . DeletedBy = user
}
2021-12-10 04:27:50 +03:00
// RemoveDeletedBranchByName removes all deleted branches
func RemoveDeletedBranchByName ( repoID int64 , branch string ) error {
2021-09-23 18:45:36 +03:00
_ , err := db . GetEngine ( db . DefaultContext ) . Where ( "repo_id=? AND name=?" , repoID , branch ) . Delete ( new ( DeletedBranch ) )
2019-12-27 22:17:37 +03:00
return err
}
2017-10-26 03:49:16 +03:00
// RemoveOldDeletedBranches removes old deleted branches
2020-05-17 02:31:38 +03:00
func RemoveOldDeletedBranches ( ctx context . Context , olderThan time . Duration ) {
2019-12-15 12:51:28 +03:00
// Nothing to do for shutdown or terminate
2017-10-26 03:49:16 +03:00
log . Trace ( "Doing: DeletedBranchesCleanup" )
2020-05-17 02:31:38 +03:00
deleteBefore := time . Now ( ) . Add ( - olderThan )
2021-09-23 18:45:36 +03:00
_ , err := db . GetEngine ( db . DefaultContext ) . Where ( "deleted_unix < ?" , deleteBefore . Unix ( ) ) . Delete ( new ( DeletedBranch ) )
2017-10-26 03:49:16 +03:00
if err != nil {
2019-04-02 10:48:31 +03:00
log . Error ( "DeletedBranchesCleanup: %v" , err )
2017-10-26 03:49:16 +03:00
}
}
2021-10-08 20:03:04 +03:00
// RenamedBranch provide renamed branch log
// will check it when a branch can't be found
type RenamedBranch struct {
ID int64 ` xorm:"pk autoincr" `
RepoID int64 ` xorm:"INDEX NOT NULL" `
From string
To string
CreatedUnix timeutil . TimeStamp ` xorm:"created" `
}
// FindRenamedBranch check if a branch was renamed
func FindRenamedBranch ( repoID int64 , from string ) ( branch * RenamedBranch , exist bool , err error ) {
branch = & RenamedBranch {
RepoID : repoID ,
From : from ,
}
exist , err = db . GetEngine ( db . DefaultContext ) . Get ( branch )
return
}
// RenameBranch rename a branch
2021-12-10 04:27:50 +03:00
func RenameBranch ( repo * repo_model . Repository , from , to string , gitAction func ( isDefault bool ) error ) ( err error ) {
2021-11-21 18:41:00 +03:00
ctx , committer , err := db . TxContext ( )
if err != nil {
2021-10-08 20:03:04 +03:00
return err
}
2021-11-21 18:41:00 +03:00
defer committer . Close ( )
2021-10-08 20:03:04 +03:00
2021-11-21 18:41:00 +03:00
sess := db . GetEngine ( ctx )
2021-10-08 20:03:04 +03:00
// 1. update default branch if needed
isDefault := repo . DefaultBranch == from
if isDefault {
repo . DefaultBranch = to
_ , err = sess . ID ( repo . ID ) . Cols ( "default_branch" ) . Update ( repo )
if err != nil {
return err
}
}
// 2. Update protected branch if needed
2022-05-20 17:08:52 +03:00
protectedBranch , err := GetProtectedBranchBy ( ctx , repo . ID , from )
2021-10-08 20:03:04 +03:00
if err != nil {
return err
}
if protectedBranch != nil {
protectedBranch . BranchName = to
_ , err = sess . ID ( protectedBranch . ID ) . Cols ( "branch_name" ) . Update ( protectedBranch )
if err != nil {
return err
}
}
// 3. Update all not merged pull request base branch name
_ , err = sess . Table ( new ( PullRequest ) ) . Where ( "base_repo_id=? AND base_branch=? AND has_merged=?" ,
repo . ID , from , false ) .
Update ( map [ string ] interface { } { "base_branch" : to } )
if err != nil {
return err
}
// 4. do git action
if err = gitAction ( isDefault ) ; err != nil {
return err
}
// 5. insert renamed branch record
renamedBranch := & RenamedBranch {
RepoID : repo . ID ,
From : from ,
To : to ,
}
2021-11-21 18:41:00 +03:00
err = db . Insert ( ctx , renamedBranch )
2021-10-08 20:03:04 +03:00
if err != nil {
return err
}
2021-11-21 18:41:00 +03:00
return committer . Commit ( )
2021-10-08 20:03:04 +03:00
}