mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 08:30:25 +08:00 
			
		
		
		
	Give user a link to create PR after push (#4716)
* Give user a link to create PR after push * Forks now create PR in the base repository + make sure PR creation is allowed * fix code style
This commit is contained in:
		
							
								
								
									
										43
									
								
								cmd/hook.go
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								cmd/hook.go
									
									
									
									
									
								
							@@ -8,6 +8,7 @@ import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strconv"
 | 
			
		||||
@@ -174,6 +175,7 @@ func runHookPostReceive(c *cli.Context) error {
 | 
			
		||||
	hookSetup("hooks/post-receive.log")
 | 
			
		||||
 | 
			
		||||
	// the environment setted on serv command
 | 
			
		||||
	repoID, _ := strconv.ParseInt(os.Getenv(models.ProtectedBranchRepoID), 10, 64)
 | 
			
		||||
	repoUser := os.Getenv(models.EnvRepoUsername)
 | 
			
		||||
	isWiki := (os.Getenv(models.EnvRepoIsWiki) == "true")
 | 
			
		||||
	repoName := os.Getenv(models.EnvRepoName)
 | 
			
		||||
@@ -211,6 +213,47 @@ func runHookPostReceive(c *cli.Context) error {
 | 
			
		||||
		}); err != nil {
 | 
			
		||||
			log.GitLogger.Error(2, "Update: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if strings.HasPrefix(refFullName, git.BranchPrefix) {
 | 
			
		||||
			branch := strings.TrimPrefix(refFullName, git.BranchPrefix)
 | 
			
		||||
			repo, pullRequestAllowed, err := private.GetRepository(repoID)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.GitLogger.Error(2, "get repo: %v", err)
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			if !pullRequestAllowed {
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			baseRepo := repo
 | 
			
		||||
			if repo.IsFork {
 | 
			
		||||
				baseRepo = repo.BaseRepo
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if !repo.IsFork && branch == baseRepo.DefaultBranch {
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			pr, err := private.ActivePullRequest(baseRepo.ID, repo.ID, baseRepo.DefaultBranch, branch)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.GitLogger.Error(2, "get active pr: %v", err)
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			fmt.Fprintln(os.Stderr, "")
 | 
			
		||||
			if pr == nil {
 | 
			
		||||
				if repo.IsFork {
 | 
			
		||||
					branch = fmt.Sprintf("%s:%s", repo.OwnerName, branch)
 | 
			
		||||
				}
 | 
			
		||||
				fmt.Fprintf(os.Stderr, "Create a new pull request for '%s':\n", branch)
 | 
			
		||||
				fmt.Fprintf(os.Stderr, "  %s/compare/%s...%s\n", baseRepo.HTMLURL(), url.QueryEscape(baseRepo.DefaultBranch), url.QueryEscape(branch))
 | 
			
		||||
			} else {
 | 
			
		||||
				fmt.Fprint(os.Stderr, "Visit the existing pull request:\n")
 | 
			
		||||
				fmt.Fprintf(os.Stderr, "  %s/pulls/%d\n", baseRepo.HTMLURL(), pr.Index)
 | 
			
		||||
			}
 | 
			
		||||
			fmt.Fprintln(os.Stderr, "")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										68
									
								
								modules/private/repository.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								modules/private/repository.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,68 @@
 | 
			
		||||
// Copyright 2018 The Gitea Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a MIT-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package private
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/url"
 | 
			
		||||
 | 
			
		||||
	"code.gitea.io/gitea/models"
 | 
			
		||||
	"code.gitea.io/gitea/modules/log"
 | 
			
		||||
	"code.gitea.io/gitea/modules/setting"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// GetRepository return the repository by its ID and a bool about if it's allowed to have PR
 | 
			
		||||
func GetRepository(repoID int64) (*models.Repository, bool, error) {
 | 
			
		||||
	reqURL := setting.LocalURL + fmt.Sprintf("api/internal/repository/%d", repoID)
 | 
			
		||||
	log.GitLogger.Trace("GetRepository: %s", reqURL)
 | 
			
		||||
 | 
			
		||||
	resp, err := newInternalRequest(reqURL, "GET").Response()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, false, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var repoInfo struct {
 | 
			
		||||
		Repository       *models.Repository
 | 
			
		||||
		AllowPullRequest bool
 | 
			
		||||
	}
 | 
			
		||||
	if err := json.NewDecoder(resp.Body).Decode(&repoInfo); err != nil {
 | 
			
		||||
		return nil, false, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer resp.Body.Close()
 | 
			
		||||
 | 
			
		||||
	// All 2XX status codes are accepted and others will return an error
 | 
			
		||||
	if resp.StatusCode/100 != 2 {
 | 
			
		||||
		return nil, false, fmt.Errorf("failed to retrieve repository: %s", decodeJSONError(resp).Err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return repoInfo.Repository, repoInfo.AllowPullRequest, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ActivePullRequest returns an active pull request if it exists
 | 
			
		||||
func ActivePullRequest(baseRepoID int64, headRepoID int64, baseBranch, headBranch string) (*models.PullRequest, error) {
 | 
			
		||||
	reqURL := setting.LocalURL + fmt.Sprintf("api/internal/active-pull-request?baseRepoID=%d&headRepoID=%d&baseBranch=%s&headBranch=%s", baseRepoID, headRepoID, url.QueryEscape(baseBranch), url.QueryEscape(headBranch))
 | 
			
		||||
	log.GitLogger.Trace("ActivePullRequest: %s", reqURL)
 | 
			
		||||
 | 
			
		||||
	resp, err := newInternalRequest(reqURL, "GET").Response()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var pr *models.PullRequest
 | 
			
		||||
	if err := json.NewDecoder(resp.Body).Decode(&pr); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer resp.Body.Close()
 | 
			
		||||
 | 
			
		||||
	// All 2XX status codes are accepted and others will return an error
 | 
			
		||||
	if resp.StatusCode/100 != 2 {
 | 
			
		||||
		return nil, fmt.Errorf("failed to retrieve pull request: %s", decodeJSONError(resp).Err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return pr, nil
 | 
			
		||||
}
 | 
			
		||||
@@ -44,5 +44,7 @@ func RegisterRoutes(m *macaron.Macaron) {
 | 
			
		||||
		m.Post("/push/update", PushUpdate)
 | 
			
		||||
		m.Get("/protectedbranch/:pbid/:userid", CanUserPush)
 | 
			
		||||
		m.Get("/branch/:id/*", GetProtectedBranchBy)
 | 
			
		||||
		m.Get("/repository/:rid", GetRepository)
 | 
			
		||||
		m.Get("/active-pull-request", GetActivePullRequest)
 | 
			
		||||
	}, CheckInternalToken)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										84
									
								
								routers/private/repository.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								routers/private/repository.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,84 @@
 | 
			
		||||
// Copyright 2018 The Gitea Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a MIT-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package private
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
 | 
			
		||||
	"code.gitea.io/gitea/models"
 | 
			
		||||
 | 
			
		||||
	macaron "gopkg.in/macaron.v1"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// GetRepository return the default branch of a repository
 | 
			
		||||
func GetRepository(ctx *macaron.Context) {
 | 
			
		||||
	repoID := ctx.ParamsInt64(":rid")
 | 
			
		||||
	repository, err := models.GetRepositoryByID(repoID)
 | 
			
		||||
	repository.MustOwnerName()
 | 
			
		||||
	allowPulls := repository.AllowsPulls()
 | 
			
		||||
	// put it back to nil because json unmarshal can't unmarshal it
 | 
			
		||||
	repository.Units = nil
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
 | 
			
		||||
			"err": err.Error(),
 | 
			
		||||
		})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if repository.IsFork {
 | 
			
		||||
		repository.GetBaseRepo()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
 | 
			
		||||
				"err": err.Error(),
 | 
			
		||||
			})
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		repository.BaseRepo.MustOwnerName()
 | 
			
		||||
		allowPulls = repository.BaseRepo.AllowsPulls()
 | 
			
		||||
		// put it back to nil because json unmarshal can't unmarshal it
 | 
			
		||||
		repository.BaseRepo.Units = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx.JSON(http.StatusOK, struct {
 | 
			
		||||
		Repository       *models.Repository
 | 
			
		||||
		AllowPullRequest bool
 | 
			
		||||
	}{
 | 
			
		||||
		Repository:       repository,
 | 
			
		||||
		AllowPullRequest: allowPulls,
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetActivePullRequest return an active pull request when it exists or an empty object
 | 
			
		||||
func GetActivePullRequest(ctx *macaron.Context) {
 | 
			
		||||
	baseRepoID := ctx.QueryInt64("baseRepoID")
 | 
			
		||||
	headRepoID := ctx.QueryInt64("headRepoID")
 | 
			
		||||
	baseBranch, err := url.QueryUnescape(ctx.QueryTrim("baseBranch"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
 | 
			
		||||
			"err": err.Error(),
 | 
			
		||||
		})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	headBranch, err := url.QueryUnescape(ctx.QueryTrim("headBranch"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
 | 
			
		||||
			"err": err.Error(),
 | 
			
		||||
		})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pr, err := models.GetUnmergedPullRequest(headRepoID, baseRepoID, headBranch, baseBranch)
 | 
			
		||||
	if err != nil && !models.IsErrPullRequestNotExist(err) {
 | 
			
		||||
		ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
 | 
			
		||||
			"err": err.Error(),
 | 
			
		||||
		})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx.JSON(http.StatusOK, pr)
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user