1
0
Fork 0
mirror of https://codeberg.org/forgejo/forgejo.git synced 2024-12-28 13:49:13 -05:00
forgejo/routers/api/v1/repo/branch.go

844 lines
24 KiB
Go
Raw Normal View History

2016-01-28 14:49:05 -05:00
// Copyright 2016 The Gogs Authors. All rights reserved.
Improve listing performance by using go-git (#6478) * Use go-git for tree reading and commit info lookup. Signed-off-by: Filip Navara <navara@emclient.com> * Use TreeEntry.IsRegular() instead of ObjectType that was removed. Signed-off-by: Filip Navara <navara@emclient.com> * Use the treePath to optimize commit info search. Signed-off-by: Filip Navara <navara@emclient.com> * Extract the latest commit at treePath along with the other commits. Signed-off-by: Filip Navara <navara@emclient.com> * Fix listing commit info for a directory that was created in one commit and never modified after. Signed-off-by: Filip Navara <navara@emclient.com> * Avoid nearly all external 'git' invocations when doing directory listing (.editorconfig code path is still hit). Signed-off-by: Filip Navara <navara@emclient.com> * Use go-git for reading blobs. Signed-off-by: Filip Navara <navara@emclient.com> * Make SHA1 type alias for plumbing.Hash in go-git. Signed-off-by: Filip Navara <navara@emclient.com> * Make Signature type alias for object.Signature in go-git. Signed-off-by: Filip Navara <navara@emclient.com> * Fix GetCommitsInfo for repository with only one commit. Signed-off-by: Filip Navara <navara@emclient.com> * Fix PGP signature verification. Signed-off-by: Filip Navara <navara@emclient.com> * Fix issues with walking commit graph across merges. Signed-off-by: Filip Navara <navara@emclient.com> * Fix typo in condition. Signed-off-by: Filip Navara <navara@emclient.com> * Speed up loading branch list by keeping the repository reference (and thus all the loaded packfile indexes). Signed-off-by: Filip Navara <navara@emclient.com> * Fix lising submodules. Signed-off-by: Filip Navara <navara@emclient.com> * Fix build Signed-off-by: Filip Navara <navara@emclient.com> * Add back commit cache because of name-rev Signed-off-by: Filip Navara <navara@emclient.com> * Fix tests Signed-off-by: Filip Navara <navara@emclient.com> * Fix code style * Fix spelling * Address PR feedback Signed-off-by: Filip Navara <navara@emclient.com> * Update vendor module list Signed-off-by: Filip Navara <navara@emclient.com> * Fix getting trees by commit id Signed-off-by: Filip Navara <navara@emclient.com> * Fix remaining unit test failures * Fix GetTreeBySHA * Avoid running `git name-rev` if not necessary Signed-off-by: Filip Navara <navara@emclient.com> * Move Branch code to git module * Clean up GPG signature verification and fix it for tagged commits * Address PR feedback (import formatting, copyright headers) * Make blob lookup by SHA working * Update tests to use public API * Allow getting content from any type of object through the blob interface * Change test to actually expect the object content that is in the GIT repository * Change one more test to actually expect the object content that is in the GIT repository * Add comments
2019-04-19 08:17:27 -04:00
// Copyright 2019 The Gitea Authors. All rights reserved.
2016-01-15 13:24:03 -05:00
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package repo
import (
"fmt"
"net/http"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert"
Improve listing performance by using go-git (#6478) * Use go-git for tree reading and commit info lookup. Signed-off-by: Filip Navara <navara@emclient.com> * Use TreeEntry.IsRegular() instead of ObjectType that was removed. Signed-off-by: Filip Navara <navara@emclient.com> * Use the treePath to optimize commit info search. Signed-off-by: Filip Navara <navara@emclient.com> * Extract the latest commit at treePath along with the other commits. Signed-off-by: Filip Navara <navara@emclient.com> * Fix listing commit info for a directory that was created in one commit and never modified after. Signed-off-by: Filip Navara <navara@emclient.com> * Avoid nearly all external 'git' invocations when doing directory listing (.editorconfig code path is still hit). Signed-off-by: Filip Navara <navara@emclient.com> * Use go-git for reading blobs. Signed-off-by: Filip Navara <navara@emclient.com> * Make SHA1 type alias for plumbing.Hash in go-git. Signed-off-by: Filip Navara <navara@emclient.com> * Make Signature type alias for object.Signature in go-git. Signed-off-by: Filip Navara <navara@emclient.com> * Fix GetCommitsInfo for repository with only one commit. Signed-off-by: Filip Navara <navara@emclient.com> * Fix PGP signature verification. Signed-off-by: Filip Navara <navara@emclient.com> * Fix issues with walking commit graph across merges. Signed-off-by: Filip Navara <navara@emclient.com> * Fix typo in condition. Signed-off-by: Filip Navara <navara@emclient.com> * Speed up loading branch list by keeping the repository reference (and thus all the loaded packfile indexes). Signed-off-by: Filip Navara <navara@emclient.com> * Fix lising submodules. Signed-off-by: Filip Navara <navara@emclient.com> * Fix build Signed-off-by: Filip Navara <navara@emclient.com> * Add back commit cache because of name-rev Signed-off-by: Filip Navara <navara@emclient.com> * Fix tests Signed-off-by: Filip Navara <navara@emclient.com> * Fix code style * Fix spelling * Address PR feedback Signed-off-by: Filip Navara <navara@emclient.com> * Update vendor module list Signed-off-by: Filip Navara <navara@emclient.com> * Fix getting trees by commit id Signed-off-by: Filip Navara <navara@emclient.com> * Fix remaining unit test failures * Fix GetTreeBySHA * Avoid running `git name-rev` if not necessary Signed-off-by: Filip Navara <navara@emclient.com> * Move Branch code to git module * Clean up GPG signature verification and fix it for tagged commits * Address PR feedback (import formatting, copyright headers) * Make blob lookup by SHA working * Update tests to use public API * Allow getting content from any type of object through the blob interface * Change test to actually expect the object content that is in the GIT repository * Change one more test to actually expect the object content that is in the GIT repository * Add comments
2019-04-19 08:17:27 -04:00
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
repo_module "code.gitea.io/gitea/modules/repository"
api "code.gitea.io/gitea/modules/structs"
[Enhancement] Allow admin to merge pr with protected file changes (#12078) * [Enhancement] Allow admin to merge pr with protected file changes As tilte, show protected message in diff page and merge box. Signed-off-by: a1012112796 <1012112796@qq.com> * remove unused ver * Update options/locale/locale_en-US.ini Co-authored-by: Cirno the Strongest <1447794+CirnoT@users.noreply.github.com> * Add TrN * Apply suggestions from code review * fix lint * Update options/locale/locale_en-US.ini Co-authored-by: zeripath <art27@cantab.net> * Apply suggestions from code review * move pr proteced files check to TestPatch * Call TestPatch when protected branches settings changed * Apply review suggestion @CirnoT * move to service @lunny * slightly restructure routers/private/hook.go Adds a lot of comments and simplifies the logic Signed-off-by: Andrew Thornton <art27@cantab.net> * placate lint Signed-off-by: Andrew Thornton <art27@cantab.net> * skip duplicate protected files check * fix check logic * slight refactor of TestPatch Signed-off-by: Andrew Thornton <art27@cantab.net> * When checking for protected files changes in TestPatch use the temporary repository Signed-off-by: Andrew Thornton <art27@cantab.net> * fix introduced issue with hook Signed-off-by: Andrew Thornton <art27@cantab.net> * Remove the check on PR index being greater than 0 as it unnecessary Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: techknowlogick <matti@mdranta.net> Co-authored-by: Cirno the Strongest <1447794+CirnoT@users.noreply.github.com> Co-authored-by: zeripath <art27@cantab.net> Co-authored-by: techknowlogick <techknowlogick@gitea.io>
2020-10-13 14:50:57 -04:00
pull_service "code.gitea.io/gitea/services/pull"
repo_service "code.gitea.io/gitea/services/repository"
2016-01-15 13:24:03 -05:00
)
2016-11-24 02:04:31 -05:00
// 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"
if ctx.Repo.TreePath != "" {
// if TreePath != "", then URL contained extra slashes
// (i.e. "master/subbranch" instead of "master"), so branch does
// not exist
2019-03-18 22:29:43 -04:00
ctx.NotFound()
return
}
branch, err := repo_module.GetBranch(ctx.Repo.Repository, ctx.Repo.BranchName)
2016-01-15 13:24:03 -05:00
if err != nil {
Improve listing performance by using go-git (#6478) * Use go-git for tree reading and commit info lookup. Signed-off-by: Filip Navara <navara@emclient.com> * Use TreeEntry.IsRegular() instead of ObjectType that was removed. Signed-off-by: Filip Navara <navara@emclient.com> * Use the treePath to optimize commit info search. Signed-off-by: Filip Navara <navara@emclient.com> * Extract the latest commit at treePath along with the other commits. Signed-off-by: Filip Navara <navara@emclient.com> * Fix listing commit info for a directory that was created in one commit and never modified after. Signed-off-by: Filip Navara <navara@emclient.com> * Avoid nearly all external 'git' invocations when doing directory listing (.editorconfig code path is still hit). Signed-off-by: Filip Navara <navara@emclient.com> * Use go-git for reading blobs. Signed-off-by: Filip Navara <navara@emclient.com> * Make SHA1 type alias for plumbing.Hash in go-git. Signed-off-by: Filip Navara <navara@emclient.com> * Make Signature type alias for object.Signature in go-git. Signed-off-by: Filip Navara <navara@emclient.com> * Fix GetCommitsInfo for repository with only one commit. Signed-off-by: Filip Navara <navara@emclient.com> * Fix PGP signature verification. Signed-off-by: Filip Navara <navara@emclient.com> * Fix issues with walking commit graph across merges. Signed-off-by: Filip Navara <navara@emclient.com> * Fix typo in condition. Signed-off-by: Filip Navara <navara@emclient.com> * Speed up loading branch list by keeping the repository reference (and thus all the loaded packfile indexes). Signed-off-by: Filip Navara <navara@emclient.com> * Fix lising submodules. Signed-off-by: Filip Navara <navara@emclient.com> * Fix build Signed-off-by: Filip Navara <navara@emclient.com> * Add back commit cache because of name-rev Signed-off-by: Filip Navara <navara@emclient.com> * Fix tests Signed-off-by: Filip Navara <navara@emclient.com> * Fix code style * Fix spelling * Address PR feedback Signed-off-by: Filip Navara <navara@emclient.com> * Update vendor module list Signed-off-by: Filip Navara <navara@emclient.com> * Fix getting trees by commit id Signed-off-by: Filip Navara <navara@emclient.com> * Fix remaining unit test failures * Fix GetTreeBySHA * Avoid running `git name-rev` if not necessary Signed-off-by: Filip Navara <navara@emclient.com> * Move Branch code to git module * Clean up GPG signature verification and fix it for tagged commits * Address PR feedback (import formatting, copyright headers) * Make blob lookup by SHA working * Update tests to use public API * Allow getting content from any type of object through the blob interface * Change test to actually expect the object content that is in the GIT repository * Change one more test to actually expect the object content that is in the GIT repository * Add comments
2019-04-19 08:17:27 -04:00
if git.IsErrBranchNotExist(err) {
2019-03-18 22:29:43 -04:00
ctx.NotFound(err)
} else {
ctx.Error(http.StatusInternalServerError, "GetBranch", err)
}
2016-01-15 13:24:03 -05:00
return
}
2016-02-02 17:07:40 -05:00
2016-01-15 13:24:03 -05:00
c, err := branch.GetCommit()
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetCommit", err)
2016-01-15 13:24:03 -05:00
return
}
2016-02-02 17:07:40 -05:00
branchProtection, err := ctx.Repo.Repository.GetBranchProtection(ctx.Repo.BranchName)
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetBranchProtection", err)
return
}
br, err := convert.ToBranch(ctx.Repo.Repository, branch, c, branchProtection, ctx.User, ctx.Repo.IsAdmin())
if err != nil {
ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err)
return
}
ctx.JSON(http.StatusOK, br)
2016-01-15 13:24:03 -05:00
}
// 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"
if ctx.Repo.TreePath != "" {
// if TreePath != "", then URL contained extra slashes
// (i.e. "master/subbranch" instead of "master"), so branch does
// not exist
ctx.NotFound()
return
}
if ctx.Repo.Repository.DefaultBranch == ctx.Repo.BranchName {
ctx.Error(http.StatusForbidden, "DefaultBranch", fmt.Errorf("can not delete default branch"))
return
}
isProtected, err := ctx.Repo.Repository.IsProtectedBranch(ctx.Repo.BranchName, ctx.User)
if err != nil {
ctx.InternalServerError(err)
return
}
if isProtected {
ctx.Error(http.StatusForbidden, "IsProtectedBranch", fmt.Errorf("branch protected"))
return
}
branch, err := repo_module.GetBranch(ctx.Repo.Repository, ctx.Repo.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
}
if err := ctx.Repo.GitRepo.DeleteBranch(ctx.Repo.BranchName, git.DeleteBranchOptions{
Force: true,
}); err != nil {
ctx.Error(http.StatusInternalServerError, "DeleteBranch", err)
return
}
// Don't return error below this
if err := repo_service.PushUpdate(
&repo_service.PushUpdateOptions{
RefFullName: git.BranchPrefix + ctx.Repo.BranchName,
OldCommitID: c.ID.String(),
NewCommitID: git.EmptySHA,
PusherID: ctx.User.ID,
PusherName: ctx.User.Name,
RepoUserName: ctx.Repo.Owner.Name,
RepoName: ctx.Repo.Repository.Name,
}); err != nil {
log.Error("Update: %v", err)
}
if err := ctx.Repo.Repository.AddDeletedBranch(ctx.Repo.BranchName, c.ID.String(), ctx.User.ID); err != nil {
log.Warn("AddDeletedBranch: %v", err)
}
ctx.Status(http.StatusNoContent)
}
// CreateBranch creates a branch for a user's repository
func CreateBranch(ctx *context.APIContext, opt api.CreateBranchRepoOption) {
// 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"
// "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 len(opt.OldBranchName) == 0 {
opt.OldBranchName = ctx.Repo.Repository.DefaultBranch
}
err := repo_module.CreateNewBranch(ctx.User, ctx.Repo.Repository, opt.OldBranchName, opt.BranchName)
if err != nil {
if models.IsErrBranchDoesNotExist(err) {
ctx.Error(http.StatusNotFound, "", "The old branch does not exist")
}
if models.IsErrTagAlreadyExists(err) {
ctx.Error(http.StatusConflict, "", "The branch with the same tag already exists.")
} else if models.IsErrBranchAlreadyExists(err) || git.IsErrPushOutOfDate(err) {
ctx.Error(http.StatusConflict, "", "The branch already exists.")
} else if models.IsErrBranchNameConflict(err) {
ctx.Error(http.StatusConflict, "", "The branch with the same name already exists.")
} else {
ctx.Error(http.StatusInternalServerError, "CreateRepoBranch", err)
}
return
}
branch, err := repo_module.GetBranch(ctx.Repo.Repository, 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 := ctx.Repo.Repository.GetBranchProtection(branch.Name)
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetBranchProtection", err)
return
}
br, err := convert.ToBranch(ctx.Repo.Repository, branch, commit, branchProtection, ctx.User, ctx.Repo.IsAdmin())
if err != nil {
ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err)
return
}
ctx.JSON(http.StatusCreated, br)
}
2016-11-24 02:04:31 -05:00
// 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
// responses:
// "200":
// "$ref": "#/responses/BranchList"
branches, err := repo_module.GetBranches(ctx.Repo.Repository)
2016-01-15 13:24:03 -05:00
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetBranches", err)
2016-01-15 13:24:03 -05:00
return
}
2016-02-02 17:07:40 -05:00
apiBranches := make([]*api.Branch, len(branches))
for i := range branches {
c, err := branches[i].GetCommit()
2016-01-15 13:24:03 -05:00
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetCommit", err)
return
2016-01-15 13:24:03 -05:00
}
branchProtection, err := ctx.Repo.Repository.GetBranchProtection(branches[i].Name)
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetBranchProtection", err)
return
}
apiBranches[i], err = convert.ToBranch(ctx.Repo.Repository, branches[i], c, branchProtection, ctx.User, ctx.Repo.IsAdmin())
if err != nil {
ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err)
return
}
2016-01-15 13:24:03 -05:00
}
2016-02-02 17:07:40 -05:00
ctx.JSON(http.StatusOK, &apiBranches)
2016-01-15 13:24:03 -05:00
}
// 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 := models.GetProtectedBranchBy(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(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 := repo.GetProtectedBranches()
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetProtectedBranches", err)
return
}
apiBps := make([]*api.BranchProtection, len(bps))
for i := range bps {
apiBps[i] = convert.ToBranchProtection(bps[i])
}
ctx.JSON(http.StatusOK, apiBps)
}
// CreateBranchProtection creates a branch protection for a repo
func CreateBranchProtection(ctx *context.APIContext, form api.CreateBranchProtectionOption) {
// 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"
repo := ctx.Repo.Repository
// Currently protection must match an actual branch
if !git.IsBranchExist(ctx.Repo.Repository.RepoPath(), form.BranchName) {
ctx.NotFound()
return
}
protectBranch, err := models.GetProtectedBranchBy(repo.ID, form.BranchName)
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 := models.GetUserIDsByNames(form.PushWhitelistUsernames, false)
if err != nil {
if models.IsErrUserNotExist(err) {
ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
return
}
ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
return
}
mergeWhitelistUsers, err := models.GetUserIDsByNames(form.MergeWhitelistUsernames, false)
if err != nil {
if models.IsErrUserNotExist(err) {
ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
return
}
ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
return
}
approvalsWhitelistUsers, err := models.GetUserIDsByNames(form.ApprovalsWhitelistUsernames, false)
if err != nil {
if models.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 = models.GetTeamIDsByNames(repo.OwnerID, form.PushWhitelistTeams, false)
if err != nil {
if models.IsErrTeamNotExist(err) {
ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
return
}
ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
return
}
mergeWhitelistTeams, err = models.GetTeamIDsByNames(repo.OwnerID, form.MergeWhitelistTeams, false)
if err != nil {
if models.IsErrTeamNotExist(err) {
ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
return
}
ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
return
}
approvalsWhitelistTeams, err = models.GetTeamIDsByNames(repo.OwnerID, form.ApprovalsWhitelistTeams, false)
if err != nil {
if models.IsErrTeamNotExist(err) {
ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
return
}
ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
return
}
}
protectBranch = &models.ProtectedBranch{
RepoID: ctx.Repo.Repository.ID,
BranchName: form.BranchName,
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,
DismissStaleApprovals: form.DismissStaleApprovals,
RequireSignedCommits: form.RequireSignedCommits,
ProtectedFilePatterns: form.ProtectedFilePatterns,
BlockOnOutdatedBranch: form.BlockOnOutdatedBranch,
}
err = models.UpdateProtectBranch(ctx.Repo.Repository, protectBranch, models.WhitelistOptions{
UserIDs: whitelistUsers,
TeamIDs: whitelistTeams,
MergeUserIDs: mergeWhitelistUsers,
MergeTeamIDs: mergeWhitelistTeams,
ApprovalsUserIDs: approvalsWhitelistUsers,
ApprovalsTeamIDs: approvalsWhitelistTeams,
})
if err != nil {
ctx.Error(http.StatusInternalServerError, "UpdateProtectBranch", err)
return
}
[Enhancement] Allow admin to merge pr with protected file changes (#12078) * [Enhancement] Allow admin to merge pr with protected file changes As tilte, show protected message in diff page and merge box. Signed-off-by: a1012112796 <1012112796@qq.com> * remove unused ver * Update options/locale/locale_en-US.ini Co-authored-by: Cirno the Strongest <1447794+CirnoT@users.noreply.github.com> * Add TrN * Apply suggestions from code review * fix lint * Update options/locale/locale_en-US.ini Co-authored-by: zeripath <art27@cantab.net> * Apply suggestions from code review * move pr proteced files check to TestPatch * Call TestPatch when protected branches settings changed * Apply review suggestion @CirnoT * move to service @lunny * slightly restructure routers/private/hook.go Adds a lot of comments and simplifies the logic Signed-off-by: Andrew Thornton <art27@cantab.net> * placate lint Signed-off-by: Andrew Thornton <art27@cantab.net> * skip duplicate protected files check * fix check logic * slight refactor of TestPatch Signed-off-by: Andrew Thornton <art27@cantab.net> * When checking for protected files changes in TestPatch use the temporary repository Signed-off-by: Andrew Thornton <art27@cantab.net> * fix introduced issue with hook Signed-off-by: Andrew Thornton <art27@cantab.net> * Remove the check on PR index being greater than 0 as it unnecessary Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: techknowlogick <matti@mdranta.net> Co-authored-by: Cirno the Strongest <1447794+CirnoT@users.noreply.github.com> Co-authored-by: zeripath <art27@cantab.net> Co-authored-by: techknowlogick <techknowlogick@gitea.io>
2020-10-13 14:50:57 -04:00
if err = pull_service.CheckPrsForBaseBranch(ctx.Repo.Repository, protectBranch.BranchName); err != nil {
ctx.Error(http.StatusInternalServerError, "CheckPrsForBaseBranch", err)
return
}
// Reload from db to get all whitelists
bp, err := models.GetProtectedBranchBy(ctx.Repo.Repository.ID, form.BranchName)
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(bp))
}
// EditBranchProtection edits a branch protection for a repo
func EditBranchProtection(ctx *context.APIContext, form api.EditBranchProtectionOption) {
// 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"
repo := ctx.Repo.Repository
bpName := ctx.Params(":name")
protectBranch, err := models.GetProtectedBranchBy(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 protectBranch.EnableStatusCheck {
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.DismissStaleApprovals != nil {
protectBranch.DismissStaleApprovals = *form.DismissStaleApprovals
}
if form.RequireSignedCommits != nil {
protectBranch.RequireSignedCommits = *form.RequireSignedCommits
}
if form.ProtectedFilePatterns != nil {
protectBranch.ProtectedFilePatterns = *form.ProtectedFilePatterns
}
if form.BlockOnOutdatedBranch != nil {
protectBranch.BlockOnOutdatedBranch = *form.BlockOnOutdatedBranch
}
var whitelistUsers []int64
if form.PushWhitelistUsernames != nil {
whitelistUsers, err = models.GetUserIDsByNames(form.PushWhitelistUsernames, false)
if err != nil {
if models.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 = models.GetUserIDsByNames(form.MergeWhitelistUsernames, false)
if err != nil {
if models.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 = models.GetUserIDsByNames(form.ApprovalsWhitelistUsernames, false)
if err != nil {
if models.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 = models.GetTeamIDsByNames(repo.OwnerID, form.PushWhitelistTeams, false)
if err != nil {
if models.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 = models.GetTeamIDsByNames(repo.OwnerID, form.MergeWhitelistTeams, false)
if err != nil {
if models.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 = models.GetTeamIDsByNames(repo.OwnerID, form.ApprovalsWhitelistTeams, false)
if err != nil {
if models.IsErrTeamNotExist(err) {
ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
return
}
ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
return
}
} else {
approvalsWhitelistTeams = protectBranch.ApprovalsWhitelistTeamIDs
}
}
err = models.UpdateProtectBranch(ctx.Repo.Repository, protectBranch, models.WhitelistOptions{
UserIDs: whitelistUsers,
TeamIDs: whitelistTeams,
MergeUserIDs: mergeWhitelistUsers,
MergeTeamIDs: mergeWhitelistTeams,
ApprovalsUserIDs: approvalsWhitelistUsers,
ApprovalsTeamIDs: approvalsWhitelistTeams,
})
if err != nil {
ctx.Error(http.StatusInternalServerError, "UpdateProtectBranch", err)
return
}
[Enhancement] Allow admin to merge pr with protected file changes (#12078) * [Enhancement] Allow admin to merge pr with protected file changes As tilte, show protected message in diff page and merge box. Signed-off-by: a1012112796 <1012112796@qq.com> * remove unused ver * Update options/locale/locale_en-US.ini Co-authored-by: Cirno the Strongest <1447794+CirnoT@users.noreply.github.com> * Add TrN * Apply suggestions from code review * fix lint * Update options/locale/locale_en-US.ini Co-authored-by: zeripath <art27@cantab.net> * Apply suggestions from code review * move pr proteced files check to TestPatch * Call TestPatch when protected branches settings changed * Apply review suggestion @CirnoT * move to service @lunny * slightly restructure routers/private/hook.go Adds a lot of comments and simplifies the logic Signed-off-by: Andrew Thornton <art27@cantab.net> * placate lint Signed-off-by: Andrew Thornton <art27@cantab.net> * skip duplicate protected files check * fix check logic * slight refactor of TestPatch Signed-off-by: Andrew Thornton <art27@cantab.net> * When checking for protected files changes in TestPatch use the temporary repository Signed-off-by: Andrew Thornton <art27@cantab.net> * fix introduced issue with hook Signed-off-by: Andrew Thornton <art27@cantab.net> * Remove the check on PR index being greater than 0 as it unnecessary Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: techknowlogick <matti@mdranta.net> Co-authored-by: Cirno the Strongest <1447794+CirnoT@users.noreply.github.com> Co-authored-by: zeripath <art27@cantab.net> Co-authored-by: techknowlogick <techknowlogick@gitea.io>
2020-10-13 14:50:57 -04:00
if err = pull_service.CheckPrsForBaseBranch(ctx.Repo.Repository, protectBranch.BranchName); err != nil {
ctx.Error(http.StatusInternalServerError, "CheckPrsForBaseBranch", err)
return
}
// Reload from db to ensure get all whitelists
bp, err := models.GetProtectedBranchBy(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(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 := models.GetProtectedBranchBy(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 := ctx.Repo.Repository.DeleteProtectedBranch(bp.ID); err != nil {
ctx.Error(http.StatusInternalServerError, "DeleteProtectedBranch", err)
return
}
ctx.Status(http.StatusNoContent)
}