summaryrefslogtreecommitdiffstats
path: root/routers/private
diff options
context:
space:
mode:
authora1012112796 <1012112796@qq.com>2021-07-28 17:42:56 +0800
committerGitHub <noreply@github.com>2021-07-28 17:42:56 +0800
commit370516883717de0e6e2087c12d368eb1465ee3b0 (patch)
tree1dd7b0946ac2bbeb3b9f13518cf2df2f4ebca348 /routers/private
parent5b2e2d29ca50ba4c11a44d4f1de18ffcf215ba1b (diff)
downloadgitea-370516883717de0e6e2087c12d368eb1465ee3b0.tar.gz
gitea-370516883717de0e6e2087c12d368eb1465ee3b0.zip
Add agit flow support in gitea (#14295)
* feature: add agit flow support ref: https://git-repo.info/en/2020/03/agit-flow-and-git-repo/ example: ```Bash git checkout -b test echo "test" >> README.md git commit -m "test" git push origin HEAD:refs/for/master -o topic=test ``` Signed-off-by: a1012112796 <1012112796@qq.com> * fix lint * simplify code add fix some nits * update merge help message * Apply suggestions from code review. Thanks @jiangxin * add forced-update message * fix lint * splite writePktLine * add refs/for/<target-branch>/<topic-branch> support also * Add test code add fix api * fix lint * fix test * skip test if git version < 2.29 * try test with git 2.30.1 * fix permission check bug * fix some nit * logic implify and test code update * fix bug * apply suggestions from code review * prepare for merge Signed-off-by: Andrew Thornton <art27@cantab.net> * fix permission check bug - test code update - apply suggestions from code review @zeripath Signed-off-by: a1012112796 <1012112796@qq.com> * fix bug when target branch isn't exist * prevent some special push and fix some nits * fix lint * try splite * Apply suggestions from code review - fix permission check - handle user rename * fix version negotiation * remane * fix template * handle empty repo * ui: fix branch link under the title * fix nits Co-authored-by: Andrew Thornton <art27@cantab.net> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Diffstat (limited to 'routers/private')
-rw-r--r--routers/private/hook.go142
-rw-r--r--routers/private/internal.go1
-rw-r--r--routers/private/serv.go6
3 files changed, 148 insertions, 1 deletions
diff --git a/routers/private/hook.go b/routers/private/hook.go
index 9f5579b6ae..4bed86f38a 100644
--- a/routers/private/hook.go
+++ b/routers/private/hook.go
@@ -23,6 +23,7 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/agit"
pull_service "code.gitea.io/gitea/services/pull"
repo_service "code.gitea.io/gitea/services/repository"
)
@@ -155,6 +156,56 @@ func HookPreReceive(ctx *gitea_context.PrivateContext) {
private.GitQuarantinePath+"="+opts.GitQuarantinePath)
}
+ if git.SupportProcReceive {
+ pusher, err := models.GetUserByID(opts.UserID)
+ if err != nil {
+ log.Error("models.GetUserByID:%v", err)
+ ctx.Error(http.StatusInternalServerError, "")
+ return
+ }
+
+ perm, err := models.GetUserRepoPermission(repo, pusher)
+ if err != nil {
+ log.Error("models.GetUserRepoPermission:%v", err)
+ ctx.Error(http.StatusInternalServerError, "")
+ return
+ }
+
+ canCreatePullRequest := perm.CanRead(models.UnitTypePullRequests)
+
+ for _, refFullName := range opts.RefFullNames {
+ // if user want update other refs (branch or tag),
+ // should check code write permission because
+ // this check was delayed.
+ if !strings.HasPrefix(refFullName, git.PullRequestPrefix) {
+ if !perm.CanWrite(models.UnitTypeCode) {
+ ctx.JSON(http.StatusForbidden, map[string]interface{}{
+ "err": "User permission denied.",
+ })
+ return
+ }
+
+ break
+ } else if repo.IsEmpty {
+ ctx.JSON(http.StatusForbidden, map[string]interface{}{
+ "err": "Can't create pull request for an empty repository.",
+ })
+ return
+ } else if !canCreatePullRequest {
+ ctx.JSON(http.StatusForbidden, map[string]interface{}{
+ "err": "User permission denied.",
+ })
+ return
+ } else if opts.IsWiki {
+ // TODO: maybe can do it ...
+ ctx.JSON(http.StatusForbidden, map[string]interface{}{
+ "err": "not support send pull request to wiki.",
+ })
+ return
+ }
+ }
+ }
+
protectedTags, err := repo.GetProtectedTags()
if err != nil {
log.Error("Unable to get protected tags for %-v Error: %v", repo, err)
@@ -392,11 +443,35 @@ func HookPreReceive(ctx *gitea_context.PrivateContext) {
})
return
}
+ } else if git.SupportProcReceive && strings.HasPrefix(refFullName, git.PullRequestPrefix) {
+ baseBranchName := opts.RefFullNames[i][len(git.PullRequestPrefix):]
+
+ baseBranchExist := false
+ if gitRepo.IsBranchExist(baseBranchName) {
+ baseBranchExist = true
+ }
+
+ if !baseBranchExist {
+ for p, v := range baseBranchName {
+ if v == '/' && gitRepo.IsBranchExist(baseBranchName[:p]) && p != len(baseBranchName)-1 {
+ baseBranchExist = true
+ break
+ }
+ }
+ }
+
+ if !baseBranchExist {
+ ctx.JSON(http.StatusForbidden, private.Response{
+ Err: fmt.Sprintf("Unexpected ref: %s", refFullName),
+ })
+ return
+ }
} else {
log.Error("Unexpected ref: %s", refFullName)
ctx.JSON(http.StatusInternalServerError, private.Response{
Err: fmt.Sprintf("Unexpected ref: %s", refFullName),
})
+ return
}
}
@@ -537,7 +612,7 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) {
continue
}
- pr, err := models.GetUnmergedPullRequest(repo.ID, baseRepo.ID, branch, baseRepo.DefaultBranch)
+ pr, err := models.GetUnmergedPullRequest(repo.ID, baseRepo.ID, branch, baseRepo.DefaultBranch, models.PullRequestFlowGithub)
if err != nil && !models.IsErrPullRequestNotExist(err) {
log.Error("Failed to get active PR in: %-v Branch: %s to: %-v Branch: %s Error: %v", repo, branch, baseRepo, baseRepo.DefaultBranch, err)
ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{
@@ -574,6 +649,30 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) {
})
}
+// HookProcReceive proc-receive hook
+func HookProcReceive(ctx *gitea_context.PrivateContext) {
+ opts := web.GetForm(ctx).(*private.HookOptions)
+ if !git.SupportProcReceive {
+ ctx.Status(http.StatusNotFound)
+ return
+ }
+
+ cancel := loadRepositoryAndGitRepoByParams(ctx)
+ if ctx.Written() {
+ return
+ }
+ defer cancel()
+
+ results := agit.ProcRecive(ctx, opts)
+ if ctx.Written() {
+ return
+ }
+
+ ctx.JSON(http.StatusOK, private.HockProcReceiveResult{
+ Results: results,
+ })
+}
+
// SetDefaultBranch updates the default branch
func SetDefaultBranch(ctx *gitea_context.PrivateContext) {
ownerName := ctx.Params(":owner")
@@ -618,3 +717,44 @@ func SetDefaultBranch(ctx *gitea_context.PrivateContext) {
}
ctx.PlainText(http.StatusOK, []byte("success"))
}
+
+func loadRepositoryAndGitRepoByParams(ctx *gitea_context.PrivateContext) context.CancelFunc {
+ ownerName := ctx.Params(":owner")
+ repoName := ctx.Params(":repo")
+
+ repo, err := models.GetRepositoryByOwnerAndName(ownerName, repoName)
+ if err != nil {
+ log.Error("Failed to get repository: %s/%s Error: %v", ownerName, repoName, err)
+ ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
+ "Err": fmt.Sprintf("Failed to get repository: %s/%s Error: %v", ownerName, repoName, err),
+ })
+ return nil
+ }
+ if repo.OwnerName == "" {
+ repo.OwnerName = ownerName
+ }
+
+ gitRepo, err := git.OpenRepository(repo.RepoPath())
+ if err != nil {
+ log.Error("Failed to open repository: %s/%s Error: %v", ownerName, repoName, err)
+ ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
+ "Err": fmt.Sprintf("Failed to open repository: %s/%s Error: %v", ownerName, repoName, err),
+ })
+ return nil
+ }
+
+ ctx.Repo = &gitea_context.Repository{
+ Repository: repo,
+ GitRepo: gitRepo,
+ }
+
+ // We opened it, we should close it
+ cancel := func() {
+ // If it's been set to nil then assume someone else has closed it.
+ if ctx.Repo.GitRepo != nil {
+ ctx.Repo.GitRepo.Close()
+ }
+ }
+
+ return cancel
+}
diff --git a/routers/private/internal.go b/routers/private/internal.go
index 9202e67218..155e8c036b 100644
--- a/routers/private/internal.go
+++ b/routers/private/internal.go
@@ -58,6 +58,7 @@ func Routes() *web.Route {
r.Post("/ssh/log", bind(private.SSHLogOption{}), SSHLog)
r.Post("/hook/pre-receive/{owner}/{repo}", bind(private.HookOptions{}), HookPreReceive)
r.Post("/hook/post-receive/{owner}/{repo}", bind(private.HookOptions{}), HookPostReceive)
+ r.Post("/hook/proc-receive/{owner}/{repo}", bind(private.HookOptions{}), HookProcReceive)
r.Post("/hook/set-default-branch/{owner}/{repo}/{branch}", SetDefaultBranch)
r.Get("/serv/none/{keyid}", ServNoCommand)
r.Get("/serv/command/{keyid}/{owner}/{repo}", ServCommand)
diff --git a/routers/private/serv.go b/routers/private/serv.go
index 6e39790eb5..f80d16a7f8 100644
--- a/routers/private/serv.go
+++ b/routers/private/serv.go
@@ -12,6 +12,7 @@ import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context"
+ "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/setting"
@@ -288,6 +289,11 @@ func ServCommand(ctx *context.PrivateContext) {
return
}
} else {
+ // Because of special ref "refs/for" .. , need delay write permission check
+ if git.SupportProcReceive && unitType == models.UnitTypeCode {
+ mode = models.AccessModeRead
+ }
+
perm, err := models.GetUserRepoPermission(repo, user)
if err != nil {
log.Error("Unable to get permissions for %-v with key %d in %-v Error: %v", user, key.ID, repo, err)