1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012 |
- // Copyright 2016 The Gogs Authors. All rights reserved.
- // Copyright 2019 The Gitea Authors. All rights reserved.
- // SPDX-License-Identifier: MIT
-
- package repo
-
- import (
- "errors"
- "fmt"
- "net/http"
-
- "code.gitea.io/gitea/models"
- git_model "code.gitea.io/gitea/models/git"
- "code.gitea.io/gitea/models/organization"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
- "code.gitea.io/gitea/modules/git"
- repo_module "code.gitea.io/gitea/modules/repository"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/convert"
- pull_service "code.gitea.io/gitea/services/pull"
- repo_service "code.gitea.io/gitea/services/repository"
- )
-
- // GetBranch get a branch of a repository
- func GetBranch(ctx *context.APIContext) {
- // swagger:operation GET /repos/{owner}/{repo}/branches/{branch} repository repoGetBranch
- // ---
- // summary: Retrieve a specific branch from a repository, including its effective branch protection
- // produces:
- // - application/json
- // parameters:
- // - name: owner
- // in: path
- // description: owner of the repo
- // type: string
- // required: true
- // - name: repo
- // in: path
- // description: name of the repo
- // type: string
- // required: true
- // - name: branch
- // in: path
- // description: branch to get
- // type: string
- // required: true
- // responses:
- // "200":
- // "$ref": "#/responses/Branch"
- // "404":
- // "$ref": "#/responses/notFound"
-
- branchName := ctx.Params("*")
-
- branch, err := ctx.Repo.GitRepo.GetBranch(branchName)
- if err != nil {
- if git.IsErrBranchNotExist(err) {
- ctx.NotFound(err)
- } else {
- ctx.Error(http.StatusInternalServerError, "GetBranch", err)
- }
- return
- }
-
- c, err := branch.GetCommit()
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetCommit", err)
- return
- }
-
- branchProtection, err := git_model.GetFirstMatchProtectedBranchRule(ctx, ctx.Repo.Repository.ID, branchName)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetBranchProtection", err)
- return
- }
-
- br, err := convert.ToBranch(ctx, ctx.Repo.Repository, branch.Name, c, branchProtection, ctx.Doer, ctx.Repo.IsAdmin())
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err)
- return
- }
-
- ctx.JSON(http.StatusOK, br)
- }
-
- // DeleteBranch get a branch of a repository
- func DeleteBranch(ctx *context.APIContext) {
- // swagger:operation DELETE /repos/{owner}/{repo}/branches/{branch} repository repoDeleteBranch
- // ---
- // summary: Delete a specific branch from a repository
- // produces:
- // - application/json
- // parameters:
- // - name: owner
- // in: path
- // description: owner of the repo
- // type: string
- // required: true
- // - name: repo
- // in: path
- // description: name of the repo
- // type: string
- // required: true
- // - name: branch
- // in: path
- // description: branch to delete
- // type: string
- // required: true
- // responses:
- // "204":
- // "$ref": "#/responses/empty"
- // "403":
- // "$ref": "#/responses/error"
- // "404":
- // "$ref": "#/responses/notFound"
-
- if ctx.Repo.Repository.IsEmpty {
- ctx.Error(http.StatusNotFound, "", "Git Repository is empty.")
- return
- }
-
- if ctx.Repo.Repository.IsArchived {
- ctx.Error(http.StatusForbidden, "", "Git Repository is archived.")
- return
- }
-
- if ctx.Repo.Repository.IsMirror {
- ctx.Error(http.StatusForbidden, "", "Git Repository is a mirror.")
- return
- }
-
- branchName := ctx.Params("*")
-
- if ctx.Repo.Repository.IsEmpty {
- ctx.Error(http.StatusForbidden, "", "Git Repository is empty.")
- return
- }
-
- // check whether branches of this repository has been synced
- totalNumOfBranches, err := git_model.CountBranches(ctx, git_model.FindBranchOptions{
- RepoID: ctx.Repo.Repository.ID,
- IsDeletedBranch: util.OptionalBoolFalse,
- })
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "CountBranches", err)
- return
- }
- if totalNumOfBranches == 0 { // sync branches immediately because non-empty repository should have at least 1 branch
- _, err = repo_module.SyncRepoBranches(ctx, ctx.Repo.Repository.ID, 0)
- if err != nil {
- ctx.ServerError("SyncRepoBranches", err)
- return
- }
- }
-
- if ctx.Repo.Repository.IsArchived {
- ctx.Error(http.StatusForbidden, "IsArchived", fmt.Errorf("can not delete branch of an archived repository"))
- return
- }
- if ctx.Repo.Repository.IsMirror {
- ctx.Error(http.StatusForbidden, "IsMirrored", fmt.Errorf("can not delete branch of an mirror repository"))
- return
- }
-
- if err := repo_service.DeleteBranch(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, branchName); err != nil {
- switch {
- case git.IsErrBranchNotExist(err):
- ctx.NotFound(err)
- case errors.Is(err, repo_service.ErrBranchIsDefault):
- ctx.Error(http.StatusForbidden, "DefaultBranch", fmt.Errorf("can not delete default branch"))
- case errors.Is(err, git_model.ErrBranchIsProtected):
- ctx.Error(http.StatusForbidden, "IsProtectedBranch", fmt.Errorf("branch protected"))
- default:
- ctx.Error(http.StatusInternalServerError, "DeleteBranch", err)
- }
- return
- }
-
- ctx.Status(http.StatusNoContent)
- }
-
- // CreateBranch creates a branch for a user's repository
- func CreateBranch(ctx *context.APIContext) {
- // swagger:operation POST /repos/{owner}/{repo}/branches repository repoCreateBranch
- // ---
- // summary: Create a branch
- // consumes:
- // - application/json
- // produces:
- // - application/json
- // parameters:
- // - name: owner
- // in: path
- // description: owner of the repo
- // type: string
- // required: true
- // - name: repo
- // in: path
- // description: name of the repo
- // type: string
- // required: true
- // - name: body
- // in: body
- // schema:
- // "$ref": "#/definitions/CreateBranchRepoOption"
- // responses:
- // "201":
- // "$ref": "#/responses/Branch"
- // "403":
- // description: The branch is archived or a mirror.
- // "404":
- // description: The old branch does not exist.
- // "409":
- // description: The branch with the same name already exists.
-
- if ctx.Repo.Repository.IsEmpty {
- ctx.Error(http.StatusNotFound, "", "Git Repository is empty.")
- return
- }
-
- if ctx.Repo.Repository.IsArchived {
- ctx.Error(http.StatusForbidden, "", "Git Repository is archived.")
- return
- }
-
- if ctx.Repo.Repository.IsMirror {
- ctx.Error(http.StatusForbidden, "", "Git Repository is a mirror.")
- return
- }
-
- opt := web.GetForm(ctx).(*api.CreateBranchRepoOption)
-
- var oldCommit *git.Commit
- var err error
-
- if len(opt.OldRefName) > 0 {
- oldCommit, err = ctx.Repo.GitRepo.GetCommit(opt.OldRefName)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetCommit", err)
- return
- }
- } else if len(opt.OldBranchName) > 0 { //nolint
- if ctx.Repo.GitRepo.IsBranchExist(opt.OldBranchName) { //nolint
- oldCommit, err = ctx.Repo.GitRepo.GetBranchCommit(opt.OldBranchName) //nolint
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetBranchCommit", err)
- return
- }
- } else {
- ctx.Error(http.StatusNotFound, "", "The old branch does not exist")
- return
- }
- } else {
- oldCommit, err = ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetBranchCommit", err)
- return
- }
- }
-
- err = repo_service.CreateNewBranchFromCommit(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, oldCommit.ID.String(), opt.BranchName)
- if err != nil {
- if git_model.IsErrBranchNotExist(err) {
- ctx.Error(http.StatusNotFound, "", "The old branch does not exist")
- } else if models.IsErrTagAlreadyExists(err) {
- ctx.Error(http.StatusConflict, "", "The branch with the same tag already exists.")
- } else if git_model.IsErrBranchAlreadyExists(err) || git.IsErrPushOutOfDate(err) {
- ctx.Error(http.StatusConflict, "", "The branch already exists.")
- } else if git_model.IsErrBranchNameConflict(err) {
- ctx.Error(http.StatusConflict, "", "The branch with the same name already exists.")
- } else {
- ctx.Error(http.StatusInternalServerError, "CreateNewBranchFromCommit", err)
- }
- return
- }
-
- branch, err := ctx.Repo.GitRepo.GetBranch(opt.BranchName)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetBranch", err)
- return
- }
-
- commit, err := branch.GetCommit()
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetCommit", err)
- return
- }
-
- branchProtection, err := git_model.GetFirstMatchProtectedBranchRule(ctx, ctx.Repo.Repository.ID, branch.Name)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetBranchProtection", err)
- return
- }
-
- br, err := convert.ToBranch(ctx, ctx.Repo.Repository, branch.Name, commit, branchProtection, ctx.Doer, ctx.Repo.IsAdmin())
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err)
- return
- }
-
- ctx.JSON(http.StatusCreated, br)
- }
-
- // ListBranches list all the branches of a repository
- func ListBranches(ctx *context.APIContext) {
- // swagger:operation GET /repos/{owner}/{repo}/branches repository repoListBranches
- // ---
- // summary: List a repository's branches
- // produces:
- // - application/json
- // parameters:
- // - name: owner
- // in: path
- // description: owner of the repo
- // type: string
- // required: true
- // - name: repo
- // in: path
- // description: name of the repo
- // type: string
- // required: true
- // - name: page
- // in: query
- // description: page number of results to return (1-based)
- // type: integer
- // - name: limit
- // in: query
- // description: page size of results
- // type: integer
- // responses:
- // "200":
- // "$ref": "#/responses/BranchList"
-
- var totalNumOfBranches int64
- var apiBranches []*api.Branch
-
- listOptions := utils.GetListOptions(ctx)
-
- if !ctx.Repo.Repository.IsEmpty {
- if ctx.Repo.GitRepo == nil {
- ctx.Error(http.StatusInternalServerError, "Load git repository failed", nil)
- return
- }
-
- branchOpts := git_model.FindBranchOptions{
- ListOptions: listOptions,
- RepoID: ctx.Repo.Repository.ID,
- IsDeletedBranch: util.OptionalBoolFalse,
- }
- var err error
- totalNumOfBranches, err = git_model.CountBranches(ctx, branchOpts)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "CountBranches", err)
- return
- }
- if totalNumOfBranches == 0 { // sync branches immediately because non-empty repository should have at least 1 branch
- totalNumOfBranches, err = repo_module.SyncRepoBranches(ctx, ctx.Repo.Repository.ID, 0)
- if err != nil {
- ctx.ServerError("SyncRepoBranches", err)
- return
- }
- }
-
- rules, err := git_model.FindRepoProtectedBranchRules(ctx, ctx.Repo.Repository.ID)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "FindMatchedProtectedBranchRules", err)
- return
- }
-
- branches, err := git_model.FindBranches(ctx, branchOpts)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetBranches", err)
- return
- }
-
- apiBranches = make([]*api.Branch, 0, len(branches))
- for i := range branches {
- c, err := ctx.Repo.GitRepo.GetBranchCommit(branches[i].Name)
- if err != nil {
- // Skip if this branch doesn't exist anymore.
- if git.IsErrNotExist(err) {
- totalNumOfBranches--
- continue
- }
- ctx.Error(http.StatusInternalServerError, "GetCommit", err)
- return
- }
-
- branchProtection := rules.GetFirstMatched(branches[i].Name)
- apiBranch, err := convert.ToBranch(ctx, ctx.Repo.Repository, branches[i].Name, c, branchProtection, ctx.Doer, ctx.Repo.IsAdmin())
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err)
- return
- }
- apiBranches = append(apiBranches, apiBranch)
- }
- }
-
- ctx.SetLinkHeader(int(totalNumOfBranches), listOptions.PageSize)
- ctx.SetTotalCountHeader(totalNumOfBranches)
- ctx.JSON(http.StatusOK, apiBranches)
- }
-
- // GetBranchProtection gets a branch protection
- func GetBranchProtection(ctx *context.APIContext) {
- // swagger:operation GET /repos/{owner}/{repo}/branch_protections/{name} repository repoGetBranchProtection
- // ---
- // summary: Get a specific branch protection for the repository
- // produces:
- // - application/json
- // parameters:
- // - name: owner
- // in: path
- // description: owner of the repo
- // type: string
- // required: true
- // - name: repo
- // in: path
- // description: name of the repo
- // type: string
- // required: true
- // - name: name
- // in: path
- // description: name of protected branch
- // type: string
- // required: true
- // responses:
- // "200":
- // "$ref": "#/responses/BranchProtection"
- // "404":
- // "$ref": "#/responses/notFound"
-
- repo := ctx.Repo.Repository
- bpName := ctx.Params(":name")
- bp, err := git_model.GetProtectedBranchRuleByName(ctx, repo.ID, bpName)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetProtectedBranchByID", err)
- return
- }
- if bp == nil || bp.RepoID != repo.ID {
- ctx.NotFound()
- return
- }
-
- ctx.JSON(http.StatusOK, convert.ToBranchProtection(ctx, bp))
- }
-
- // ListBranchProtections list branch protections for a repo
- func ListBranchProtections(ctx *context.APIContext) {
- // swagger:operation GET /repos/{owner}/{repo}/branch_protections repository repoListBranchProtection
- // ---
- // summary: List branch protections for a repository
- // produces:
- // - application/json
- // parameters:
- // - name: owner
- // in: path
- // description: owner of the repo
- // type: string
- // required: true
- // - name: repo
- // in: path
- // description: name of the repo
- // type: string
- // required: true
- // responses:
- // "200":
- // "$ref": "#/responses/BranchProtectionList"
-
- repo := ctx.Repo.Repository
- bps, err := git_model.FindRepoProtectedBranchRules(ctx, repo.ID)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetProtectedBranches", err)
- return
- }
- apiBps := make([]*api.BranchProtection, len(bps))
- for i := range bps {
- apiBps[i] = convert.ToBranchProtection(ctx, bps[i])
- }
-
- ctx.JSON(http.StatusOK, apiBps)
- }
-
- // CreateBranchProtection creates a branch protection for a repo
- func CreateBranchProtection(ctx *context.APIContext) {
- // swagger:operation POST /repos/{owner}/{repo}/branch_protections repository repoCreateBranchProtection
- // ---
- // summary: Create a branch protections for a repository
- // consumes:
- // - application/json
- // produces:
- // - application/json
- // parameters:
- // - name: owner
- // in: path
- // description: owner of the repo
- // type: string
- // required: true
- // - name: repo
- // in: path
- // description: name of the repo
- // type: string
- // required: true
- // - name: body
- // in: body
- // schema:
- // "$ref": "#/definitions/CreateBranchProtectionOption"
- // responses:
- // "201":
- // "$ref": "#/responses/BranchProtection"
- // "403":
- // "$ref": "#/responses/forbidden"
- // "404":
- // "$ref": "#/responses/notFound"
- // "422":
- // "$ref": "#/responses/validationError"
-
- form := web.GetForm(ctx).(*api.CreateBranchProtectionOption)
- repo := ctx.Repo.Repository
-
- ruleName := form.RuleName
- if ruleName == "" {
- ruleName = form.BranchName //nolint
- }
- if len(ruleName) == 0 {
- ctx.Error(http.StatusBadRequest, "both rule_name and branch_name are empty", "both rule_name and branch_name are empty")
- return
- }
-
- isPlainRule := !git_model.IsRuleNameSpecial(ruleName)
- var isBranchExist bool
- if isPlainRule {
- isBranchExist = git.IsBranchExist(ctx.Req.Context(), ctx.Repo.Repository.RepoPath(), ruleName)
- }
-
- protectBranch, err := git_model.GetProtectedBranchRuleByName(ctx, repo.ID, ruleName)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetProtectBranchOfRepoByName", err)
- return
- } else if protectBranch != nil {
- ctx.Error(http.StatusForbidden, "Create branch protection", "Branch protection already exist")
- return
- }
-
- var requiredApprovals int64
- if form.RequiredApprovals > 0 {
- requiredApprovals = form.RequiredApprovals
- }
-
- whitelistUsers, err := user_model.GetUserIDsByNames(ctx, form.PushWhitelistUsernames, false)
- if err != nil {
- if user_model.IsErrUserNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
- return
- }
- ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
- return
- }
- mergeWhitelistUsers, err := user_model.GetUserIDsByNames(ctx, form.MergeWhitelistUsernames, false)
- if err != nil {
- if user_model.IsErrUserNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
- return
- }
- ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
- return
- }
- approvalsWhitelistUsers, err := user_model.GetUserIDsByNames(ctx, form.ApprovalsWhitelistUsernames, false)
- if err != nil {
- if user_model.IsErrUserNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
- return
- }
- ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
- return
- }
- var whitelistTeams, mergeWhitelistTeams, approvalsWhitelistTeams []int64
- if repo.Owner.IsOrganization() {
- whitelistTeams, err = organization.GetTeamIDsByNames(repo.OwnerID, form.PushWhitelistTeams, false)
- if err != nil {
- if organization.IsErrTeamNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
- return
- }
- ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
- return
- }
- mergeWhitelistTeams, err = organization.GetTeamIDsByNames(repo.OwnerID, form.MergeWhitelistTeams, false)
- if err != nil {
- if organization.IsErrTeamNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
- return
- }
- ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
- return
- }
- approvalsWhitelistTeams, err = organization.GetTeamIDsByNames(repo.OwnerID, form.ApprovalsWhitelistTeams, false)
- if err != nil {
- if organization.IsErrTeamNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
- return
- }
- ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
- return
- }
- }
-
- protectBranch = &git_model.ProtectedBranch{
- RepoID: ctx.Repo.Repository.ID,
- RuleName: ruleName,
- CanPush: form.EnablePush,
- EnableWhitelist: form.EnablePush && form.EnablePushWhitelist,
- EnableMergeWhitelist: form.EnableMergeWhitelist,
- WhitelistDeployKeys: form.EnablePush && form.EnablePushWhitelist && form.PushWhitelistDeployKeys,
- EnableStatusCheck: form.EnableStatusCheck,
- StatusCheckContexts: form.StatusCheckContexts,
- EnableApprovalsWhitelist: form.EnableApprovalsWhitelist,
- RequiredApprovals: requiredApprovals,
- BlockOnRejectedReviews: form.BlockOnRejectedReviews,
- BlockOnOfficialReviewRequests: form.BlockOnOfficialReviewRequests,
- DismissStaleApprovals: form.DismissStaleApprovals,
- RequireSignedCommits: form.RequireSignedCommits,
- ProtectedFilePatterns: form.ProtectedFilePatterns,
- UnprotectedFilePatterns: form.UnprotectedFilePatterns,
- BlockOnOutdatedBranch: form.BlockOnOutdatedBranch,
- }
-
- err = git_model.UpdateProtectBranch(ctx, ctx.Repo.Repository, protectBranch, git_model.WhitelistOptions{
- UserIDs: whitelistUsers,
- TeamIDs: whitelistTeams,
- MergeUserIDs: mergeWhitelistUsers,
- MergeTeamIDs: mergeWhitelistTeams,
- ApprovalsUserIDs: approvalsWhitelistUsers,
- ApprovalsTeamIDs: approvalsWhitelistTeams,
- })
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "UpdateProtectBranch", err)
- return
- }
-
- if isBranchExist {
- if err = pull_service.CheckPRsForBaseBranch(ctx, ctx.Repo.Repository, ruleName); err != nil {
- ctx.Error(http.StatusInternalServerError, "CheckPRsForBaseBranch", err)
- return
- }
- } else {
- if !isPlainRule {
- if ctx.Repo.GitRepo == nil {
- ctx.Repo.GitRepo, err = git.OpenRepository(ctx, ctx.Repo.Repository.RepoPath())
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "OpenRepository", err)
- return
- }
- defer func() {
- ctx.Repo.GitRepo.Close()
- ctx.Repo.GitRepo = nil
- }()
- }
- // FIXME: since we only need to recheck files protected rules, we could improve this
- matchedBranches, err := git_model.FindAllMatchedBranches(ctx, ctx.Repo.Repository.ID, ruleName)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "FindAllMatchedBranches", err)
- return
- }
-
- for _, branchName := range matchedBranches {
- if err = pull_service.CheckPRsForBaseBranch(ctx, ctx.Repo.Repository, branchName); err != nil {
- ctx.Error(http.StatusInternalServerError, "CheckPRsForBaseBranch", err)
- return
- }
- }
- }
- }
-
- // Reload from db to get all whitelists
- bp, err := git_model.GetProtectedBranchRuleByName(ctx, ctx.Repo.Repository.ID, ruleName)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetProtectedBranchByID", err)
- return
- }
- if bp == nil || bp.RepoID != ctx.Repo.Repository.ID {
- ctx.Error(http.StatusInternalServerError, "New branch protection not found", err)
- return
- }
-
- ctx.JSON(http.StatusCreated, convert.ToBranchProtection(ctx, bp))
- }
-
- // EditBranchProtection edits a branch protection for a repo
- func EditBranchProtection(ctx *context.APIContext) {
- // swagger:operation PATCH /repos/{owner}/{repo}/branch_protections/{name} repository repoEditBranchProtection
- // ---
- // summary: Edit a branch protections for a repository. Only fields that are set will be changed
- // consumes:
- // - application/json
- // produces:
- // - application/json
- // parameters:
- // - name: owner
- // in: path
- // description: owner of the repo
- // type: string
- // required: true
- // - name: repo
- // in: path
- // description: name of the repo
- // type: string
- // required: true
- // - name: name
- // in: path
- // description: name of protected branch
- // type: string
- // required: true
- // - name: body
- // in: body
- // schema:
- // "$ref": "#/definitions/EditBranchProtectionOption"
- // responses:
- // "200":
- // "$ref": "#/responses/BranchProtection"
- // "404":
- // "$ref": "#/responses/notFound"
- // "422":
- // "$ref": "#/responses/validationError"
- form := web.GetForm(ctx).(*api.EditBranchProtectionOption)
- repo := ctx.Repo.Repository
- bpName := ctx.Params(":name")
- protectBranch, err := git_model.GetProtectedBranchRuleByName(ctx, repo.ID, bpName)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetProtectedBranchByID", err)
- return
- }
- if protectBranch == nil || protectBranch.RepoID != repo.ID {
- ctx.NotFound()
- return
- }
-
- if form.EnablePush != nil {
- if !*form.EnablePush {
- protectBranch.CanPush = false
- protectBranch.EnableWhitelist = false
- protectBranch.WhitelistDeployKeys = false
- } else {
- protectBranch.CanPush = true
- if form.EnablePushWhitelist != nil {
- if !*form.EnablePushWhitelist {
- protectBranch.EnableWhitelist = false
- protectBranch.WhitelistDeployKeys = false
- } else {
- protectBranch.EnableWhitelist = true
- if form.PushWhitelistDeployKeys != nil {
- protectBranch.WhitelistDeployKeys = *form.PushWhitelistDeployKeys
- }
- }
- }
- }
- }
-
- if form.EnableMergeWhitelist != nil {
- protectBranch.EnableMergeWhitelist = *form.EnableMergeWhitelist
- }
-
- if form.EnableStatusCheck != nil {
- protectBranch.EnableStatusCheck = *form.EnableStatusCheck
- }
-
- if form.StatusCheckContexts != nil {
- protectBranch.StatusCheckContexts = form.StatusCheckContexts
- }
-
- if form.RequiredApprovals != nil && *form.RequiredApprovals >= 0 {
- protectBranch.RequiredApprovals = *form.RequiredApprovals
- }
-
- if form.EnableApprovalsWhitelist != nil {
- protectBranch.EnableApprovalsWhitelist = *form.EnableApprovalsWhitelist
- }
-
- if form.BlockOnRejectedReviews != nil {
- protectBranch.BlockOnRejectedReviews = *form.BlockOnRejectedReviews
- }
-
- if form.BlockOnOfficialReviewRequests != nil {
- protectBranch.BlockOnOfficialReviewRequests = *form.BlockOnOfficialReviewRequests
- }
-
- if form.DismissStaleApprovals != nil {
- protectBranch.DismissStaleApprovals = *form.DismissStaleApprovals
- }
-
- if form.RequireSignedCommits != nil {
- protectBranch.RequireSignedCommits = *form.RequireSignedCommits
- }
-
- if form.ProtectedFilePatterns != nil {
- protectBranch.ProtectedFilePatterns = *form.ProtectedFilePatterns
- }
-
- if form.UnprotectedFilePatterns != nil {
- protectBranch.UnprotectedFilePatterns = *form.UnprotectedFilePatterns
- }
-
- if form.BlockOnOutdatedBranch != nil {
- protectBranch.BlockOnOutdatedBranch = *form.BlockOnOutdatedBranch
- }
-
- var whitelistUsers []int64
- if form.PushWhitelistUsernames != nil {
- whitelistUsers, err = user_model.GetUserIDsByNames(ctx, form.PushWhitelistUsernames, false)
- if err != nil {
- if user_model.IsErrUserNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
- return
- }
- ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
- return
- }
- } else {
- whitelistUsers = protectBranch.WhitelistUserIDs
- }
- var mergeWhitelistUsers []int64
- if form.MergeWhitelistUsernames != nil {
- mergeWhitelistUsers, err = user_model.GetUserIDsByNames(ctx, form.MergeWhitelistUsernames, false)
- if err != nil {
- if user_model.IsErrUserNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
- return
- }
- ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
- return
- }
- } else {
- mergeWhitelistUsers = protectBranch.MergeWhitelistUserIDs
- }
- var approvalsWhitelistUsers []int64
- if form.ApprovalsWhitelistUsernames != nil {
- approvalsWhitelistUsers, err = user_model.GetUserIDsByNames(ctx, form.ApprovalsWhitelistUsernames, false)
- if err != nil {
- if user_model.IsErrUserNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
- return
- }
- ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
- return
- }
- } else {
- approvalsWhitelistUsers = protectBranch.ApprovalsWhitelistUserIDs
- }
-
- var whitelistTeams, mergeWhitelistTeams, approvalsWhitelistTeams []int64
- if repo.Owner.IsOrganization() {
- if form.PushWhitelistTeams != nil {
- whitelistTeams, err = organization.GetTeamIDsByNames(repo.OwnerID, form.PushWhitelistTeams, false)
- if err != nil {
- if organization.IsErrTeamNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
- return
- }
- ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
- return
- }
- } else {
- whitelistTeams = protectBranch.WhitelistTeamIDs
- }
- if form.MergeWhitelistTeams != nil {
- mergeWhitelistTeams, err = organization.GetTeamIDsByNames(repo.OwnerID, form.MergeWhitelistTeams, false)
- if err != nil {
- if organization.IsErrTeamNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
- return
- }
- ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
- return
- }
- } else {
- mergeWhitelistTeams = protectBranch.MergeWhitelistTeamIDs
- }
- if form.ApprovalsWhitelistTeams != nil {
- approvalsWhitelistTeams, err = organization.GetTeamIDsByNames(repo.OwnerID, form.ApprovalsWhitelistTeams, false)
- if err != nil {
- if organization.IsErrTeamNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
- return
- }
- ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
- return
- }
- } else {
- approvalsWhitelistTeams = protectBranch.ApprovalsWhitelistTeamIDs
- }
- }
-
- err = git_model.UpdateProtectBranch(ctx, ctx.Repo.Repository, protectBranch, git_model.WhitelistOptions{
- UserIDs: whitelistUsers,
- TeamIDs: whitelistTeams,
- MergeUserIDs: mergeWhitelistUsers,
- MergeTeamIDs: mergeWhitelistTeams,
- ApprovalsUserIDs: approvalsWhitelistUsers,
- ApprovalsTeamIDs: approvalsWhitelistTeams,
- })
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "UpdateProtectBranch", err)
- return
- }
-
- isPlainRule := !git_model.IsRuleNameSpecial(bpName)
- var isBranchExist bool
- if isPlainRule {
- isBranchExist = git.IsBranchExist(ctx.Req.Context(), ctx.Repo.Repository.RepoPath(), bpName)
- }
-
- if isBranchExist {
- if err = pull_service.CheckPRsForBaseBranch(ctx, ctx.Repo.Repository, bpName); err != nil {
- ctx.Error(http.StatusInternalServerError, "CheckPrsForBaseBranch", err)
- return
- }
- } else {
- if !isPlainRule {
- if ctx.Repo.GitRepo == nil {
- ctx.Repo.GitRepo, err = git.OpenRepository(ctx, ctx.Repo.Repository.RepoPath())
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "OpenRepository", err)
- return
- }
- defer func() {
- ctx.Repo.GitRepo.Close()
- ctx.Repo.GitRepo = nil
- }()
- }
-
- // FIXME: since we only need to recheck files protected rules, we could improve this
- matchedBranches, err := git_model.FindAllMatchedBranches(ctx, ctx.Repo.Repository.ID, protectBranch.RuleName)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "FindAllMatchedBranches", err)
- return
- }
-
- for _, branchName := range matchedBranches {
- if err = pull_service.CheckPRsForBaseBranch(ctx, ctx.Repo.Repository, branchName); err != nil {
- ctx.Error(http.StatusInternalServerError, "CheckPrsForBaseBranch", err)
- return
- }
- }
- }
- }
-
- // Reload from db to ensure get all whitelists
- bp, err := git_model.GetProtectedBranchRuleByName(ctx, repo.ID, bpName)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetProtectedBranchBy", err)
- return
- }
- if bp == nil || bp.RepoID != ctx.Repo.Repository.ID {
- ctx.Error(http.StatusInternalServerError, "New branch protection not found", err)
- return
- }
-
- ctx.JSON(http.StatusOK, convert.ToBranchProtection(ctx, bp))
- }
-
- // DeleteBranchProtection deletes a branch protection for a repo
- func DeleteBranchProtection(ctx *context.APIContext) {
- // swagger:operation DELETE /repos/{owner}/{repo}/branch_protections/{name} repository repoDeleteBranchProtection
- // ---
- // summary: Delete a specific branch protection for the repository
- // produces:
- // - application/json
- // parameters:
- // - name: owner
- // in: path
- // description: owner of the repo
- // type: string
- // required: true
- // - name: repo
- // in: path
- // description: name of the repo
- // type: string
- // required: true
- // - name: name
- // in: path
- // description: name of protected branch
- // type: string
- // required: true
- // responses:
- // "204":
- // "$ref": "#/responses/empty"
- // "404":
- // "$ref": "#/responses/notFound"
-
- repo := ctx.Repo.Repository
- bpName := ctx.Params(":name")
- bp, err := git_model.GetProtectedBranchRuleByName(ctx, repo.ID, bpName)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetProtectedBranchByID", err)
- return
- }
- if bp == nil || bp.RepoID != repo.ID {
- ctx.NotFound()
- return
- }
-
- if err := git_model.DeleteProtectedBranch(ctx, ctx.Repo.Repository.ID, bp.ID); err != nil {
- ctx.Error(http.StatusInternalServerError, "DeleteProtectedBranch", err)
- return
- }
-
- ctx.Status(http.StatusNoContent)
- }
|