You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

setting_protected_branch.go 9.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  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. package repo
  5. import (
  6. "fmt"
  7. "net/http"
  8. "strings"
  9. "time"
  10. "code.gitea.io/gitea/models"
  11. "code.gitea.io/gitea/modules/base"
  12. "code.gitea.io/gitea/modules/context"
  13. "code.gitea.io/gitea/modules/git"
  14. "code.gitea.io/gitea/modules/log"
  15. "code.gitea.io/gitea/modules/setting"
  16. "code.gitea.io/gitea/modules/web"
  17. "code.gitea.io/gitea/services/forms"
  18. pull_service "code.gitea.io/gitea/services/pull"
  19. )
  20. // ProtectedBranch render the page to protect the repository
  21. func ProtectedBranch(ctx *context.Context) {
  22. ctx.Data["Title"] = ctx.Tr("repo.settings")
  23. ctx.Data["PageIsSettingsBranches"] = true
  24. protectedBranches, err := ctx.Repo.Repository.GetProtectedBranches()
  25. if err != nil {
  26. ctx.ServerError("GetProtectedBranches", err)
  27. return
  28. }
  29. ctx.Data["ProtectedBranches"] = protectedBranches
  30. branches := ctx.Data["Branches"].([]string)
  31. leftBranches := make([]string, 0, len(branches)-len(protectedBranches))
  32. for _, b := range branches {
  33. var protected bool
  34. for _, pb := range protectedBranches {
  35. if b == pb.BranchName {
  36. protected = true
  37. break
  38. }
  39. }
  40. if !protected {
  41. leftBranches = append(leftBranches, b)
  42. }
  43. }
  44. ctx.Data["LeftBranches"] = leftBranches
  45. ctx.HTML(http.StatusOK, tplBranches)
  46. }
  47. // ProtectedBranchPost response for protect for a branch of a repository
  48. func ProtectedBranchPost(ctx *context.Context) {
  49. ctx.Data["Title"] = ctx.Tr("repo.settings")
  50. ctx.Data["PageIsSettingsBranches"] = true
  51. repo := ctx.Repo.Repository
  52. switch ctx.FormString("action") {
  53. case "default_branch":
  54. if ctx.HasError() {
  55. ctx.HTML(http.StatusOK, tplBranches)
  56. return
  57. }
  58. branch := ctx.FormString("branch")
  59. if !ctx.Repo.GitRepo.IsBranchExist(branch) {
  60. ctx.Status(404)
  61. return
  62. } else if repo.DefaultBranch != branch {
  63. repo.DefaultBranch = branch
  64. if err := ctx.Repo.GitRepo.SetDefaultBranch(branch); err != nil {
  65. if !git.IsErrUnsupportedVersion(err) {
  66. ctx.ServerError("SetDefaultBranch", err)
  67. return
  68. }
  69. }
  70. if err := repo.UpdateDefaultBranch(); err != nil {
  71. ctx.ServerError("SetDefaultBranch", err)
  72. return
  73. }
  74. }
  75. log.Trace("Repository basic settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name)
  76. ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
  77. ctx.Redirect(setting.AppSubURL + ctx.Req.URL.Path)
  78. default:
  79. ctx.NotFound("", nil)
  80. }
  81. }
  82. // SettingsProtectedBranch renders the protected branch setting page
  83. func SettingsProtectedBranch(c *context.Context) {
  84. branch := c.Params("*")
  85. if !c.Repo.GitRepo.IsBranchExist(branch) {
  86. c.NotFound("IsBranchExist", nil)
  87. return
  88. }
  89. c.Data["Title"] = c.Tr("repo.settings.protected_branch") + " - " + branch
  90. c.Data["PageIsSettingsBranches"] = true
  91. protectBranch, err := models.GetProtectedBranchBy(c.Repo.Repository.ID, branch)
  92. if err != nil {
  93. if !git.IsErrBranchNotExist(err) {
  94. c.ServerError("GetProtectBranchOfRepoByName", err)
  95. return
  96. }
  97. }
  98. if protectBranch == nil {
  99. // No options found, create defaults.
  100. protectBranch = &models.ProtectedBranch{
  101. BranchName: branch,
  102. }
  103. }
  104. users, err := c.Repo.Repository.GetReaders()
  105. if err != nil {
  106. c.ServerError("Repo.Repository.GetReaders", err)
  107. return
  108. }
  109. c.Data["Users"] = users
  110. c.Data["whitelist_users"] = strings.Join(base.Int64sToStrings(protectBranch.WhitelistUserIDs), ",")
  111. c.Data["merge_whitelist_users"] = strings.Join(base.Int64sToStrings(protectBranch.MergeWhitelistUserIDs), ",")
  112. c.Data["approvals_whitelist_users"] = strings.Join(base.Int64sToStrings(protectBranch.ApprovalsWhitelistUserIDs), ",")
  113. contexts, _ := models.FindRepoRecentCommitStatusContexts(c.Repo.Repository.ID, 7*24*time.Hour) // Find last week status check contexts
  114. for _, ctx := range protectBranch.StatusCheckContexts {
  115. var found bool
  116. for i := range contexts {
  117. if contexts[i] == ctx {
  118. found = true
  119. break
  120. }
  121. }
  122. if !found {
  123. contexts = append(contexts, ctx)
  124. }
  125. }
  126. c.Data["branch_status_check_contexts"] = contexts
  127. c.Data["is_context_required"] = func(context string) bool {
  128. for _, c := range protectBranch.StatusCheckContexts {
  129. if c == context {
  130. return true
  131. }
  132. }
  133. return false
  134. }
  135. if c.Repo.Owner.IsOrganization() {
  136. teams, err := c.Repo.Owner.TeamsWithAccessToRepo(c.Repo.Repository.ID, models.AccessModeRead)
  137. if err != nil {
  138. c.ServerError("Repo.Owner.TeamsWithAccessToRepo", err)
  139. return
  140. }
  141. c.Data["Teams"] = teams
  142. c.Data["whitelist_teams"] = strings.Join(base.Int64sToStrings(protectBranch.WhitelistTeamIDs), ",")
  143. c.Data["merge_whitelist_teams"] = strings.Join(base.Int64sToStrings(protectBranch.MergeWhitelistTeamIDs), ",")
  144. c.Data["approvals_whitelist_teams"] = strings.Join(base.Int64sToStrings(protectBranch.ApprovalsWhitelistTeamIDs), ",")
  145. }
  146. c.Data["Branch"] = protectBranch
  147. c.HTML(http.StatusOK, tplProtectedBranch)
  148. }
  149. // SettingsProtectedBranchPost updates the protected branch settings
  150. func SettingsProtectedBranchPost(ctx *context.Context) {
  151. f := web.GetForm(ctx).(*forms.ProtectBranchForm)
  152. branch := ctx.Params("*")
  153. if !ctx.Repo.GitRepo.IsBranchExist(branch) {
  154. ctx.NotFound("IsBranchExist", nil)
  155. return
  156. }
  157. protectBranch, err := models.GetProtectedBranchBy(ctx.Repo.Repository.ID, branch)
  158. if err != nil {
  159. if !git.IsErrBranchNotExist(err) {
  160. ctx.ServerError("GetProtectBranchOfRepoByName", err)
  161. return
  162. }
  163. }
  164. if f.Protected {
  165. if protectBranch == nil {
  166. // No options found, create defaults.
  167. protectBranch = &models.ProtectedBranch{
  168. RepoID: ctx.Repo.Repository.ID,
  169. BranchName: branch,
  170. }
  171. }
  172. if f.RequiredApprovals < 0 {
  173. ctx.Flash.Error(ctx.Tr("repo.settings.protected_branch_required_approvals_min"))
  174. ctx.Redirect(fmt.Sprintf("%s/settings/branches/%s", ctx.Repo.RepoLink, branch))
  175. }
  176. var whitelistUsers, whitelistTeams, mergeWhitelistUsers, mergeWhitelistTeams, approvalsWhitelistUsers, approvalsWhitelistTeams []int64
  177. switch f.EnablePush {
  178. case "all":
  179. protectBranch.CanPush = true
  180. protectBranch.EnableWhitelist = false
  181. protectBranch.WhitelistDeployKeys = false
  182. case "whitelist":
  183. protectBranch.CanPush = true
  184. protectBranch.EnableWhitelist = true
  185. protectBranch.WhitelistDeployKeys = f.WhitelistDeployKeys
  186. if strings.TrimSpace(f.WhitelistUsers) != "" {
  187. whitelistUsers, _ = base.StringsToInt64s(strings.Split(f.WhitelistUsers, ","))
  188. }
  189. if strings.TrimSpace(f.WhitelistTeams) != "" {
  190. whitelistTeams, _ = base.StringsToInt64s(strings.Split(f.WhitelistTeams, ","))
  191. }
  192. default:
  193. protectBranch.CanPush = false
  194. protectBranch.EnableWhitelist = false
  195. protectBranch.WhitelistDeployKeys = false
  196. }
  197. protectBranch.EnableMergeWhitelist = f.EnableMergeWhitelist
  198. if f.EnableMergeWhitelist {
  199. if strings.TrimSpace(f.MergeWhitelistUsers) != "" {
  200. mergeWhitelistUsers, _ = base.StringsToInt64s(strings.Split(f.MergeWhitelistUsers, ","))
  201. }
  202. if strings.TrimSpace(f.MergeWhitelistTeams) != "" {
  203. mergeWhitelistTeams, _ = base.StringsToInt64s(strings.Split(f.MergeWhitelistTeams, ","))
  204. }
  205. }
  206. protectBranch.EnableStatusCheck = f.EnableStatusCheck
  207. if f.EnableStatusCheck {
  208. protectBranch.StatusCheckContexts = f.StatusCheckContexts
  209. } else {
  210. protectBranch.StatusCheckContexts = nil
  211. }
  212. protectBranch.RequiredApprovals = f.RequiredApprovals
  213. protectBranch.EnableApprovalsWhitelist = f.EnableApprovalsWhitelist
  214. if f.EnableApprovalsWhitelist {
  215. if strings.TrimSpace(f.ApprovalsWhitelistUsers) != "" {
  216. approvalsWhitelistUsers, _ = base.StringsToInt64s(strings.Split(f.ApprovalsWhitelistUsers, ","))
  217. }
  218. if strings.TrimSpace(f.ApprovalsWhitelistTeams) != "" {
  219. approvalsWhitelistTeams, _ = base.StringsToInt64s(strings.Split(f.ApprovalsWhitelistTeams, ","))
  220. }
  221. }
  222. protectBranch.BlockOnRejectedReviews = f.BlockOnRejectedReviews
  223. protectBranch.BlockOnOfficialReviewRequests = f.BlockOnOfficialReviewRequests
  224. protectBranch.DismissStaleApprovals = f.DismissStaleApprovals
  225. protectBranch.RequireSignedCommits = f.RequireSignedCommits
  226. protectBranch.ProtectedFilePatterns = f.ProtectedFilePatterns
  227. protectBranch.UnprotectedFilePatterns = f.UnprotectedFilePatterns
  228. protectBranch.BlockOnOutdatedBranch = f.BlockOnOutdatedBranch
  229. err = models.UpdateProtectBranch(ctx.Repo.Repository, protectBranch, models.WhitelistOptions{
  230. UserIDs: whitelistUsers,
  231. TeamIDs: whitelistTeams,
  232. MergeUserIDs: mergeWhitelistUsers,
  233. MergeTeamIDs: mergeWhitelistTeams,
  234. ApprovalsUserIDs: approvalsWhitelistUsers,
  235. ApprovalsTeamIDs: approvalsWhitelistTeams,
  236. })
  237. if err != nil {
  238. ctx.ServerError("UpdateProtectBranch", err)
  239. return
  240. }
  241. if err = pull_service.CheckPrsForBaseBranch(ctx.Repo.Repository, protectBranch.BranchName); err != nil {
  242. ctx.ServerError("CheckPrsForBaseBranch", err)
  243. return
  244. }
  245. ctx.Flash.Success(ctx.Tr("repo.settings.update_protect_branch_success", branch))
  246. ctx.Redirect(fmt.Sprintf("%s/settings/branches/%s", ctx.Repo.RepoLink, branch))
  247. } else {
  248. if protectBranch != nil {
  249. if err := ctx.Repo.Repository.DeleteProtectedBranch(protectBranch.ID); err != nil {
  250. ctx.ServerError("DeleteProtectedBranch", err)
  251. return
  252. }
  253. }
  254. ctx.Flash.Success(ctx.Tr("repo.settings.remove_protected_branch_success", branch))
  255. ctx.Redirect(fmt.Sprintf("%s/settings/branches", ctx.Repo.RepoLink))
  256. }
  257. }