1// Copyright 2017 The Gitea Authors. All rights reserved. 2// Use of this source code is governed by a MIT-style 3// license that can be found in the LICENSE file. 4 5package repo 6 7import ( 8 "fmt" 9 "net/http" 10 "strings" 11 "time" 12 13 "code.gitea.io/gitea/models" 14 "code.gitea.io/gitea/models/perm" 15 repo_model "code.gitea.io/gitea/models/repo" 16 "code.gitea.io/gitea/modules/base" 17 "code.gitea.io/gitea/modules/context" 18 "code.gitea.io/gitea/modules/git" 19 "code.gitea.io/gitea/modules/log" 20 "code.gitea.io/gitea/modules/setting" 21 "code.gitea.io/gitea/modules/util" 22 "code.gitea.io/gitea/modules/web" 23 "code.gitea.io/gitea/services/forms" 24 pull_service "code.gitea.io/gitea/services/pull" 25 "code.gitea.io/gitea/services/repository" 26) 27 28// ProtectedBranch render the page to protect the repository 29func ProtectedBranch(ctx *context.Context) { 30 ctx.Data["Title"] = ctx.Tr("repo.settings") 31 ctx.Data["PageIsSettingsBranches"] = true 32 33 protectedBranches, err := models.GetProtectedBranches(ctx.Repo.Repository.ID) 34 if err != nil { 35 ctx.ServerError("GetProtectedBranches", err) 36 return 37 } 38 ctx.Data["ProtectedBranches"] = protectedBranches 39 40 branches := ctx.Data["Branches"].([]string) 41 leftBranches := make([]string, 0, len(branches)-len(protectedBranches)) 42 for _, b := range branches { 43 var protected bool 44 for _, pb := range protectedBranches { 45 if b == pb.BranchName { 46 protected = true 47 break 48 } 49 } 50 if !protected { 51 leftBranches = append(leftBranches, b) 52 } 53 } 54 55 ctx.Data["LeftBranches"] = leftBranches 56 57 ctx.HTML(http.StatusOK, tplBranches) 58} 59 60// ProtectedBranchPost response for protect for a branch of a repository 61func ProtectedBranchPost(ctx *context.Context) { 62 ctx.Data["Title"] = ctx.Tr("repo.settings") 63 ctx.Data["PageIsSettingsBranches"] = true 64 65 repo := ctx.Repo.Repository 66 67 switch ctx.FormString("action") { 68 case "default_branch": 69 if ctx.HasError() { 70 ctx.HTML(http.StatusOK, tplBranches) 71 return 72 } 73 74 branch := ctx.FormString("branch") 75 if !ctx.Repo.GitRepo.IsBranchExist(branch) { 76 ctx.Status(404) 77 return 78 } else if repo.DefaultBranch != branch { 79 repo.DefaultBranch = branch 80 if err := ctx.Repo.GitRepo.SetDefaultBranch(branch); err != nil { 81 if !git.IsErrUnsupportedVersion(err) { 82 ctx.ServerError("SetDefaultBranch", err) 83 return 84 } 85 } 86 if err := repo_model.UpdateDefaultBranch(repo); err != nil { 87 ctx.ServerError("SetDefaultBranch", err) 88 return 89 } 90 } 91 92 log.Trace("Repository basic settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name) 93 94 ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success")) 95 ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath()) 96 default: 97 ctx.NotFound("", nil) 98 } 99} 100 101// SettingsProtectedBranch renders the protected branch setting page 102func SettingsProtectedBranch(c *context.Context) { 103 branch := c.Params("*") 104 if !c.Repo.GitRepo.IsBranchExist(branch) { 105 c.NotFound("IsBranchExist", nil) 106 return 107 } 108 109 c.Data["Title"] = c.Tr("repo.settings.protected_branch") + " - " + branch 110 c.Data["PageIsSettingsBranches"] = true 111 112 protectBranch, err := models.GetProtectedBranchBy(c.Repo.Repository.ID, branch) 113 if err != nil { 114 if !git.IsErrBranchNotExist(err) { 115 c.ServerError("GetProtectBranchOfRepoByName", err) 116 return 117 } 118 } 119 120 if protectBranch == nil { 121 // No options found, create defaults. 122 protectBranch = &models.ProtectedBranch{ 123 BranchName: branch, 124 } 125 } 126 127 users, err := models.GetRepoReaders(c.Repo.Repository) 128 if err != nil { 129 c.ServerError("Repo.Repository.GetReaders", err) 130 return 131 } 132 c.Data["Users"] = users 133 c.Data["whitelist_users"] = strings.Join(base.Int64sToStrings(protectBranch.WhitelistUserIDs), ",") 134 c.Data["merge_whitelist_users"] = strings.Join(base.Int64sToStrings(protectBranch.MergeWhitelistUserIDs), ",") 135 c.Data["approvals_whitelist_users"] = strings.Join(base.Int64sToStrings(protectBranch.ApprovalsWhitelistUserIDs), ",") 136 contexts, _ := models.FindRepoRecentCommitStatusContexts(c.Repo.Repository.ID, 7*24*time.Hour) // Find last week status check contexts 137 for _, ctx := range protectBranch.StatusCheckContexts { 138 var found bool 139 for i := range contexts { 140 if contexts[i] == ctx { 141 found = true 142 break 143 } 144 } 145 if !found { 146 contexts = append(contexts, ctx) 147 } 148 } 149 150 c.Data["branch_status_check_contexts"] = contexts 151 c.Data["is_context_required"] = func(context string) bool { 152 for _, c := range protectBranch.StatusCheckContexts { 153 if c == context { 154 return true 155 } 156 } 157 return false 158 } 159 160 if c.Repo.Owner.IsOrganization() { 161 teams, err := models.OrgFromUser(c.Repo.Owner).TeamsWithAccessToRepo(c.Repo.Repository.ID, perm.AccessModeRead) 162 if err != nil { 163 c.ServerError("Repo.Owner.TeamsWithAccessToRepo", err) 164 return 165 } 166 c.Data["Teams"] = teams 167 c.Data["whitelist_teams"] = strings.Join(base.Int64sToStrings(protectBranch.WhitelistTeamIDs), ",") 168 c.Data["merge_whitelist_teams"] = strings.Join(base.Int64sToStrings(protectBranch.MergeWhitelistTeamIDs), ",") 169 c.Data["approvals_whitelist_teams"] = strings.Join(base.Int64sToStrings(protectBranch.ApprovalsWhitelistTeamIDs), ",") 170 } 171 172 c.Data["Branch"] = protectBranch 173 c.HTML(http.StatusOK, tplProtectedBranch) 174} 175 176// SettingsProtectedBranchPost updates the protected branch settings 177func SettingsProtectedBranchPost(ctx *context.Context) { 178 f := web.GetForm(ctx).(*forms.ProtectBranchForm) 179 branch := ctx.Params("*") 180 if !ctx.Repo.GitRepo.IsBranchExist(branch) { 181 ctx.NotFound("IsBranchExist", nil) 182 return 183 } 184 185 protectBranch, err := models.GetProtectedBranchBy(ctx.Repo.Repository.ID, branch) 186 if err != nil { 187 if !git.IsErrBranchNotExist(err) { 188 ctx.ServerError("GetProtectBranchOfRepoByName", err) 189 return 190 } 191 } 192 193 if f.Protected { 194 if protectBranch == nil { 195 // No options found, create defaults. 196 protectBranch = &models.ProtectedBranch{ 197 RepoID: ctx.Repo.Repository.ID, 198 BranchName: branch, 199 } 200 } 201 if f.RequiredApprovals < 0 { 202 ctx.Flash.Error(ctx.Tr("repo.settings.protected_branch_required_approvals_min")) 203 ctx.Redirect(fmt.Sprintf("%s/settings/branches/%s", ctx.Repo.RepoLink, util.PathEscapeSegments(branch))) 204 } 205 206 var whitelistUsers, whitelistTeams, mergeWhitelistUsers, mergeWhitelistTeams, approvalsWhitelistUsers, approvalsWhitelistTeams []int64 207 switch f.EnablePush { 208 case "all": 209 protectBranch.CanPush = true 210 protectBranch.EnableWhitelist = false 211 protectBranch.WhitelistDeployKeys = false 212 case "whitelist": 213 protectBranch.CanPush = true 214 protectBranch.EnableWhitelist = true 215 protectBranch.WhitelistDeployKeys = f.WhitelistDeployKeys 216 if strings.TrimSpace(f.WhitelistUsers) != "" { 217 whitelistUsers, _ = base.StringsToInt64s(strings.Split(f.WhitelistUsers, ",")) 218 } 219 if strings.TrimSpace(f.WhitelistTeams) != "" { 220 whitelistTeams, _ = base.StringsToInt64s(strings.Split(f.WhitelistTeams, ",")) 221 } 222 default: 223 protectBranch.CanPush = false 224 protectBranch.EnableWhitelist = false 225 protectBranch.WhitelistDeployKeys = false 226 } 227 228 protectBranch.EnableMergeWhitelist = f.EnableMergeWhitelist 229 if f.EnableMergeWhitelist { 230 if strings.TrimSpace(f.MergeWhitelistUsers) != "" { 231 mergeWhitelistUsers, _ = base.StringsToInt64s(strings.Split(f.MergeWhitelistUsers, ",")) 232 } 233 if strings.TrimSpace(f.MergeWhitelistTeams) != "" { 234 mergeWhitelistTeams, _ = base.StringsToInt64s(strings.Split(f.MergeWhitelistTeams, ",")) 235 } 236 } 237 238 protectBranch.EnableStatusCheck = f.EnableStatusCheck 239 if f.EnableStatusCheck { 240 protectBranch.StatusCheckContexts = f.StatusCheckContexts 241 } else { 242 protectBranch.StatusCheckContexts = nil 243 } 244 245 protectBranch.RequiredApprovals = f.RequiredApprovals 246 protectBranch.EnableApprovalsWhitelist = f.EnableApprovalsWhitelist 247 if f.EnableApprovalsWhitelist { 248 if strings.TrimSpace(f.ApprovalsWhitelistUsers) != "" { 249 approvalsWhitelistUsers, _ = base.StringsToInt64s(strings.Split(f.ApprovalsWhitelistUsers, ",")) 250 } 251 if strings.TrimSpace(f.ApprovalsWhitelistTeams) != "" { 252 approvalsWhitelistTeams, _ = base.StringsToInt64s(strings.Split(f.ApprovalsWhitelistTeams, ",")) 253 } 254 } 255 protectBranch.BlockOnRejectedReviews = f.BlockOnRejectedReviews 256 protectBranch.BlockOnOfficialReviewRequests = f.BlockOnOfficialReviewRequests 257 protectBranch.DismissStaleApprovals = f.DismissStaleApprovals 258 protectBranch.RequireSignedCommits = f.RequireSignedCommits 259 protectBranch.ProtectedFilePatterns = f.ProtectedFilePatterns 260 protectBranch.UnprotectedFilePatterns = f.UnprotectedFilePatterns 261 protectBranch.BlockOnOutdatedBranch = f.BlockOnOutdatedBranch 262 263 err = models.UpdateProtectBranch(ctx.Repo.Repository, protectBranch, models.WhitelistOptions{ 264 UserIDs: whitelistUsers, 265 TeamIDs: whitelistTeams, 266 MergeUserIDs: mergeWhitelistUsers, 267 MergeTeamIDs: mergeWhitelistTeams, 268 ApprovalsUserIDs: approvalsWhitelistUsers, 269 ApprovalsTeamIDs: approvalsWhitelistTeams, 270 }) 271 if err != nil { 272 ctx.ServerError("UpdateProtectBranch", err) 273 return 274 } 275 if err = pull_service.CheckPrsForBaseBranch(ctx.Repo.Repository, protectBranch.BranchName); err != nil { 276 ctx.ServerError("CheckPrsForBaseBranch", err) 277 return 278 } 279 ctx.Flash.Success(ctx.Tr("repo.settings.update_protect_branch_success", branch)) 280 ctx.Redirect(fmt.Sprintf("%s/settings/branches/%s", ctx.Repo.RepoLink, util.PathEscapeSegments(branch))) 281 } else { 282 if protectBranch != nil { 283 if err := models.DeleteProtectedBranch(ctx.Repo.Repository.ID, protectBranch.ID); err != nil { 284 ctx.ServerError("DeleteProtectedBranch", err) 285 return 286 } 287 } 288 ctx.Flash.Success(ctx.Tr("repo.settings.remove_protected_branch_success", branch)) 289 ctx.Redirect(fmt.Sprintf("%s/settings/branches", ctx.Repo.RepoLink)) 290 } 291} 292 293// RenameBranchPost responses for rename a branch 294func RenameBranchPost(ctx *context.Context) { 295 form := web.GetForm(ctx).(*forms.RenameBranchForm) 296 297 if !ctx.Repo.CanCreateBranch() { 298 ctx.NotFound("RenameBranch", nil) 299 return 300 } 301 302 if ctx.HasError() { 303 ctx.Flash.Error(ctx.GetErrMsg()) 304 ctx.Redirect(fmt.Sprintf("%s/settings/branches", ctx.Repo.RepoLink)) 305 return 306 } 307 308 msg, err := repository.RenameBranch(ctx.Repo.Repository, ctx.User, ctx.Repo.GitRepo, form.From, form.To) 309 if err != nil { 310 ctx.ServerError("RenameBranch", err) 311 return 312 } 313 314 if msg == "target_exist" { 315 ctx.Flash.Error(ctx.Tr("repo.settings.rename_branch_failed_exist", form.To)) 316 ctx.Redirect(fmt.Sprintf("%s/settings/branches", ctx.Repo.RepoLink)) 317 return 318 } 319 320 if msg == "from_not_exist" { 321 ctx.Flash.Error(ctx.Tr("repo.settings.rename_branch_failed_not_exist", form.From)) 322 ctx.Redirect(fmt.Sprintf("%s/settings/branches", ctx.Repo.RepoLink)) 323 return 324 } 325 326 ctx.Flash.Success(ctx.Tr("repo.settings.rename_branch_success", form.From, form.To)) 327 ctx.Redirect(fmt.Sprintf("%s/settings/branches", ctx.Repo.RepoLink)) 328} 329