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:
		
							parent
							
								
									cabdf84f1f
								
							
						
					
					
						commit
						dea3d849e1
					
				
					 4 changed files with 197 additions and 0 deletions
				
			
		
							
								
								
									
										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) | ||||
| } | ||||
		Loading…
	
		Reference in a new issue