Batch hook pre- and post-receive calls (#8602)
* make notifyWatchers work on multiple actions * more efficient multiple notifyWatchers * Make CommitRepoAction take advantage of multiple actions * Batch post and pre-receive results * Set batch to 30 * Auto adjust timeout & add logging * adjust processing message * Add some messages to pre-receive * Make any non-200 status code from pre-receive an error * Add missing hookPrintResults * Remove shortcut for single action * mistaken merge fix * oops * Move master branch to the front * If repo was empty and the master branch is pushed ensure that that is set as the default branch * fixup * fixup * Missed HookOptions in setdefaultbranch * Batch PushUpdateAddTag and PushUpdateDelTag Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
This commit is contained in:
		
					parent
					
						
							
								114d474f02
							
						
					
				
			
			
				commit
				
					
						7bfb83e064
					
				
			
		
					 10 changed files with 1063 additions and 440 deletions
				
			
		
							
								
								
									
										223
									
								
								cmd/hook.go
									
										
									
									
									
								
							
							
						
						
									
										223
									
								
								cmd/hook.go
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -21,6 +21,10 @@ import (
 | 
			
		|||
	"github.com/urfave/cli"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	hookBatchSize = 30
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	// CmdHook represents the available hooks sub-command.
 | 
			
		||||
	CmdHook = cli.Command{
 | 
			
		||||
| 
						 | 
				
			
			@ -75,12 +79,25 @@ Gitea or set your environment appropriately.`, "")
 | 
			
		|||
	prID, _ := strconv.ParseInt(os.Getenv(models.ProtectedBranchPRID), 10, 64)
 | 
			
		||||
	isDeployKey, _ := strconv.ParseBool(os.Getenv(models.EnvIsDeployKey))
 | 
			
		||||
 | 
			
		||||
	buf := bytes.NewBuffer(nil)
 | 
			
		||||
	scanner := bufio.NewScanner(os.Stdin)
 | 
			
		||||
	for scanner.Scan() {
 | 
			
		||||
		buf.Write(scanner.Bytes())
 | 
			
		||||
		buf.WriteByte('\n')
 | 
			
		||||
	hookOptions := private.HookOptions{
 | 
			
		||||
		UserID:                          userID,
 | 
			
		||||
		GitAlternativeObjectDirectories: os.Getenv(private.GitAlternativeObjectDirectories),
 | 
			
		||||
		GitObjectDirectory:              os.Getenv(private.GitObjectDirectory),
 | 
			
		||||
		GitQuarantinePath:               os.Getenv(private.GitQuarantinePath),
 | 
			
		||||
		ProtectedBranchID:               prID,
 | 
			
		||||
		IsDeployKey:                     isDeployKey,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	scanner := bufio.NewScanner(os.Stdin)
 | 
			
		||||
 | 
			
		||||
	oldCommitIDs := make([]string, hookBatchSize)
 | 
			
		||||
	newCommitIDs := make([]string, hookBatchSize)
 | 
			
		||||
	refFullNames := make([]string, hookBatchSize)
 | 
			
		||||
	count := 0
 | 
			
		||||
	total := 0
 | 
			
		||||
	lastline := 0
 | 
			
		||||
 | 
			
		||||
	for scanner.Scan() {
 | 
			
		||||
		// TODO: support news feeds for wiki
 | 
			
		||||
		if isWiki {
 | 
			
		||||
			continue
 | 
			
		||||
| 
						 | 
				
			
			@ -94,29 +111,72 @@ Gitea or set your environment appropriately.`, "")
 | 
			
		|||
		oldCommitID := string(fields[0])
 | 
			
		||||
		newCommitID := string(fields[1])
 | 
			
		||||
		refFullName := string(fields[2])
 | 
			
		||||
		total++
 | 
			
		||||
		lastline++
 | 
			
		||||
 | 
			
		||||
		// If the ref is a branch, check if it's protected
 | 
			
		||||
		if strings.HasPrefix(refFullName, git.BranchPrefix) {
 | 
			
		||||
			statusCode, msg := private.HookPreReceive(username, reponame, private.HookOptions{
 | 
			
		||||
				OldCommitID:                     oldCommitID,
 | 
			
		||||
				NewCommitID:                     newCommitID,
 | 
			
		||||
				RefFullName:                     refFullName,
 | 
			
		||||
				UserID:                          userID,
 | 
			
		||||
				GitAlternativeObjectDirectories: os.Getenv(private.GitAlternativeObjectDirectories),
 | 
			
		||||
				GitObjectDirectory:              os.Getenv(private.GitObjectDirectory),
 | 
			
		||||
				GitQuarantinePath:               os.Getenv(private.GitQuarantinePath),
 | 
			
		||||
				ProtectedBranchID:               prID,
 | 
			
		||||
				IsDeployKey:                     isDeployKey,
 | 
			
		||||
			})
 | 
			
		||||
			switch statusCode {
 | 
			
		||||
			case http.StatusInternalServerError:
 | 
			
		||||
				fail("Internal Server Error", msg)
 | 
			
		||||
			case http.StatusForbidden:
 | 
			
		||||
				fail(msg, "")
 | 
			
		||||
			oldCommitIDs[count] = oldCommitID
 | 
			
		||||
			newCommitIDs[count] = newCommitID
 | 
			
		||||
			refFullNames[count] = refFullName
 | 
			
		||||
			count++
 | 
			
		||||
			fmt.Fprintf(os.Stdout, "*")
 | 
			
		||||
			os.Stdout.Sync()
 | 
			
		||||
 | 
			
		||||
			if count >= hookBatchSize {
 | 
			
		||||
				fmt.Fprintf(os.Stdout, " Checking %d branches\n", count)
 | 
			
		||||
				os.Stdout.Sync()
 | 
			
		||||
 | 
			
		||||
				hookOptions.OldCommitIDs = oldCommitIDs
 | 
			
		||||
				hookOptions.NewCommitIDs = newCommitIDs
 | 
			
		||||
				hookOptions.RefFullNames = refFullNames
 | 
			
		||||
				statusCode, msg := private.HookPreReceive(username, reponame, hookOptions)
 | 
			
		||||
				switch statusCode {
 | 
			
		||||
				case http.StatusOK:
 | 
			
		||||
					// no-op
 | 
			
		||||
				case http.StatusInternalServerError:
 | 
			
		||||
					fail("Internal Server Error", msg)
 | 
			
		||||
				default:
 | 
			
		||||
					fail(msg, "")
 | 
			
		||||
				}
 | 
			
		||||
				count = 0
 | 
			
		||||
				lastline = 0
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			fmt.Fprintf(os.Stdout, ".")
 | 
			
		||||
			os.Stdout.Sync()
 | 
			
		||||
		}
 | 
			
		||||
		if lastline >= hookBatchSize {
 | 
			
		||||
			fmt.Fprintf(os.Stdout, "\n")
 | 
			
		||||
			os.Stdout.Sync()
 | 
			
		||||
			lastline = 0
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if count > 0 {
 | 
			
		||||
		hookOptions.OldCommitIDs = oldCommitIDs[:count]
 | 
			
		||||
		hookOptions.NewCommitIDs = newCommitIDs[:count]
 | 
			
		||||
		hookOptions.RefFullNames = refFullNames[:count]
 | 
			
		||||
 | 
			
		||||
		fmt.Fprintf(os.Stdout, " Checking %d branches\n", count)
 | 
			
		||||
		os.Stdout.Sync()
 | 
			
		||||
 | 
			
		||||
		statusCode, msg := private.HookPreReceive(username, reponame, hookOptions)
 | 
			
		||||
		switch statusCode {
 | 
			
		||||
		case http.StatusInternalServerError:
 | 
			
		||||
			fail("Internal Server Error", msg)
 | 
			
		||||
		case http.StatusForbidden:
 | 
			
		||||
			fail(msg, "")
 | 
			
		||||
		}
 | 
			
		||||
	} else if lastline > 0 {
 | 
			
		||||
		fmt.Fprintf(os.Stdout, "\n")
 | 
			
		||||
		os.Stdout.Sync()
 | 
			
		||||
		lastline = 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fmt.Fprintf(os.Stdout, "Checked %d references in total\n", total)
 | 
			
		||||
	os.Stdout.Sync()
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -156,12 +216,24 @@ Gitea or set your environment appropriately.`, "")
 | 
			
		|||
	pusherID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
 | 
			
		||||
	pusherName := os.Getenv(models.EnvPusherName)
 | 
			
		||||
 | 
			
		||||
	buf := bytes.NewBuffer(nil)
 | 
			
		||||
	hookOptions := private.HookOptions{
 | 
			
		||||
		UserName:                        pusherName,
 | 
			
		||||
		UserID:                          pusherID,
 | 
			
		||||
		GitAlternativeObjectDirectories: os.Getenv(private.GitAlternativeObjectDirectories),
 | 
			
		||||
		GitObjectDirectory:              os.Getenv(private.GitObjectDirectory),
 | 
			
		||||
		GitQuarantinePath:               os.Getenv(private.GitQuarantinePath),
 | 
			
		||||
	}
 | 
			
		||||
	oldCommitIDs := make([]string, hookBatchSize)
 | 
			
		||||
	newCommitIDs := make([]string, hookBatchSize)
 | 
			
		||||
	refFullNames := make([]string, hookBatchSize)
 | 
			
		||||
	count := 0
 | 
			
		||||
	total := 0
 | 
			
		||||
	wasEmpty := false
 | 
			
		||||
	masterPushed := false
 | 
			
		||||
	results := make([]private.HookPostReceiveBranchResult, 0)
 | 
			
		||||
 | 
			
		||||
	scanner := bufio.NewScanner(os.Stdin)
 | 
			
		||||
	for scanner.Scan() {
 | 
			
		||||
		buf.Write(scanner.Bytes())
 | 
			
		||||
		buf.WriteByte('\n')
 | 
			
		||||
 | 
			
		||||
		// TODO: support news feeds for wiki
 | 
			
		||||
		if isWiki {
 | 
			
		||||
			continue
 | 
			
		||||
| 
						 | 
				
			
			@ -172,36 +244,95 @@ Gitea or set your environment appropriately.`, "")
 | 
			
		|||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		oldCommitID := string(fields[0])
 | 
			
		||||
		newCommitID := string(fields[1])
 | 
			
		||||
		refFullName := string(fields[2])
 | 
			
		||||
 | 
			
		||||
		res, err := private.HookPostReceive(repoUser, repoName, private.HookOptions{
 | 
			
		||||
			OldCommitID: oldCommitID,
 | 
			
		||||
			NewCommitID: newCommitID,
 | 
			
		||||
			RefFullName: refFullName,
 | 
			
		||||
			UserID:      pusherID,
 | 
			
		||||
			UserName:    pusherName,
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
		if res == nil {
 | 
			
		||||
			fail("Internal Server Error", err)
 | 
			
		||||
		fmt.Fprintf(os.Stdout, ".")
 | 
			
		||||
		oldCommitIDs[count] = string(fields[0])
 | 
			
		||||
		newCommitIDs[count] = string(fields[1])
 | 
			
		||||
		refFullNames[count] = string(fields[2])
 | 
			
		||||
		if refFullNames[count] == git.BranchPrefix+"master" && newCommitIDs[count] != git.EmptySHA && count == total {
 | 
			
		||||
			masterPushed = true
 | 
			
		||||
		}
 | 
			
		||||
		count++
 | 
			
		||||
		total++
 | 
			
		||||
		os.Stdout.Sync()
 | 
			
		||||
 | 
			
		||||
		if res["message"] == false {
 | 
			
		||||
		if count >= hookBatchSize {
 | 
			
		||||
			fmt.Fprintf(os.Stdout, " Processing %d references\n", count)
 | 
			
		||||
			os.Stdout.Sync()
 | 
			
		||||
			hookOptions.OldCommitIDs = oldCommitIDs
 | 
			
		||||
			hookOptions.NewCommitIDs = newCommitIDs
 | 
			
		||||
			hookOptions.RefFullNames = refFullNames
 | 
			
		||||
			resp, err := private.HookPostReceive(repoUser, repoName, hookOptions)
 | 
			
		||||
			if resp == nil {
 | 
			
		||||
				hookPrintResults(results)
 | 
			
		||||
				fail("Internal Server Error", err)
 | 
			
		||||
			}
 | 
			
		||||
			wasEmpty = wasEmpty || resp.RepoWasEmpty
 | 
			
		||||
			results = append(results, resp.Results...)
 | 
			
		||||
			count = 0
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if count == 0 {
 | 
			
		||||
		if wasEmpty && masterPushed {
 | 
			
		||||
			// We need to tell the repo to reset the default branch to master
 | 
			
		||||
			err := private.SetDefaultBranch(repoUser, repoName, "master")
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				fail("Internal Server Error", "SetDefaultBranch failed with Error: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		fmt.Fprintf(os.Stdout, "Processed %d references in total\n", total)
 | 
			
		||||
		os.Stdout.Sync()
 | 
			
		||||
 | 
			
		||||
		hookPrintResults(results)
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hookOptions.OldCommitIDs = oldCommitIDs[:count]
 | 
			
		||||
	hookOptions.NewCommitIDs = newCommitIDs[:count]
 | 
			
		||||
	hookOptions.RefFullNames = refFullNames[:count]
 | 
			
		||||
 | 
			
		||||
	fmt.Fprintf(os.Stdout, " Processing %d references\n", count)
 | 
			
		||||
	os.Stdout.Sync()
 | 
			
		||||
 | 
			
		||||
	resp, err := private.HookPostReceive(repoUser, repoName, hookOptions)
 | 
			
		||||
	if resp == nil {
 | 
			
		||||
		hookPrintResults(results)
 | 
			
		||||
		fail("Internal Server Error", err)
 | 
			
		||||
	}
 | 
			
		||||
	wasEmpty = wasEmpty || resp.RepoWasEmpty
 | 
			
		||||
	results = append(results, resp.Results...)
 | 
			
		||||
 | 
			
		||||
	fmt.Fprintf(os.Stdout, "Processed %d references in total\n", total)
 | 
			
		||||
	os.Stdout.Sync()
 | 
			
		||||
 | 
			
		||||
	if wasEmpty && masterPushed {
 | 
			
		||||
		// We need to tell the repo to reset the default branch to master
 | 
			
		||||
		err := private.SetDefaultBranch(repoUser, repoName, "master")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fail("Internal Server Error", "SetDefaultBranch failed with Error: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hookPrintResults(results)
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func hookPrintResults(results []private.HookPostReceiveBranchResult) {
 | 
			
		||||
	for _, res := range results {
 | 
			
		||||
		if !res.Message {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		fmt.Fprintln(os.Stderr, "")
 | 
			
		||||
		if res["create"] == true {
 | 
			
		||||
			fmt.Fprintf(os.Stderr, "Create a new pull request for '%s':\n", res["branch"])
 | 
			
		||||
			fmt.Fprintf(os.Stderr, "  %s\n", res["url"])
 | 
			
		||||
		if res.Create {
 | 
			
		||||
			fmt.Fprintf(os.Stderr, "Create a new pull request for '%s':\n", res.Branch)
 | 
			
		||||
			fmt.Fprintf(os.Stderr, "  %s\n", res.URL)
 | 
			
		||||
		} else {
 | 
			
		||||
			fmt.Fprint(os.Stderr, "Visit the existing pull request:\n")
 | 
			
		||||
			fmt.Fprintf(os.Stderr, "  %s\n", res["url"])
 | 
			
		||||
			fmt.Fprintf(os.Stderr, "  %s\n", res.URL)
 | 
			
		||||
		}
 | 
			
		||||
		fmt.Fprintln(os.Stderr, "")
 | 
			
		||||
		os.Stderr.Sync()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -164,68 +164,111 @@ func (repo *Repository) GetWatchers(page int) ([]*User, error) {
 | 
			
		|||
	return users, sess.Find(&users)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func notifyWatchers(e Engine, act *Action) error {
 | 
			
		||||
	// Add feeds for user self and all watchers.
 | 
			
		||||
	watches, err := getWatchers(e, act.RepoID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("get watchers: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
func notifyWatchers(e Engine, actions ...*Action) error {
 | 
			
		||||
	var watchers []*Watch
 | 
			
		||||
	var repo *Repository
 | 
			
		||||
	var err error
 | 
			
		||||
	var permCode []bool
 | 
			
		||||
	var permIssue []bool
 | 
			
		||||
	var permPR []bool
 | 
			
		||||
 | 
			
		||||
	// Add feed for actioner.
 | 
			
		||||
	act.UserID = act.ActUserID
 | 
			
		||||
	if _, err = e.InsertOne(act); err != nil {
 | 
			
		||||
		return fmt.Errorf("insert new actioner: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	for _, act := range actions {
 | 
			
		||||
		repoChanged := repo == nil || repo.ID != act.RepoID
 | 
			
		||||
 | 
			
		||||
	act.loadRepo()
 | 
			
		||||
	// check repo owner exist.
 | 
			
		||||
	if err := act.Repo.getOwner(e); err != nil {
 | 
			
		||||
		return fmt.Errorf("can't get repo owner: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
		if repoChanged {
 | 
			
		||||
			// Add feeds for user self and all watchers.
 | 
			
		||||
			watchers, err = getWatchers(e, act.RepoID)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return fmt.Errorf("get watchers: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	// Add feed for organization
 | 
			
		||||
	if act.Repo.Owner.IsOrganization() && act.ActUserID != act.Repo.Owner.ID {
 | 
			
		||||
		act.ID = 0
 | 
			
		||||
		act.UserID = act.Repo.Owner.ID
 | 
			
		||||
		// Add feed for actioner.
 | 
			
		||||
		act.UserID = act.ActUserID
 | 
			
		||||
		if _, err = e.InsertOne(act); err != nil {
 | 
			
		||||
			return fmt.Errorf("insert new actioner: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i := range watches {
 | 
			
		||||
		if act.ActUserID == watches[i].UserID {
 | 
			
		||||
			continue
 | 
			
		||||
		if repoChanged {
 | 
			
		||||
			act.loadRepo()
 | 
			
		||||
			repo = act.Repo
 | 
			
		||||
 | 
			
		||||
			// check repo owner exist.
 | 
			
		||||
			if err := act.Repo.getOwner(e); err != nil {
 | 
			
		||||
				return fmt.Errorf("can't get repo owner: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
		} else if act.Repo == nil {
 | 
			
		||||
			act.Repo = repo
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		act.ID = 0
 | 
			
		||||
		act.UserID = watches[i].UserID
 | 
			
		||||
		act.Repo.Units = nil
 | 
			
		||||
 | 
			
		||||
		switch act.OpType {
 | 
			
		||||
		case ActionCommitRepo, ActionPushTag, ActionDeleteTag, ActionDeleteBranch:
 | 
			
		||||
			if !act.Repo.checkUnitUser(e, act.UserID, false, UnitTypeCode) {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
		case ActionCreateIssue, ActionCommentIssue, ActionCloseIssue, ActionReopenIssue:
 | 
			
		||||
			if !act.Repo.checkUnitUser(e, act.UserID, false, UnitTypeIssues) {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
		case ActionCreatePullRequest, ActionCommentPull, ActionMergePullRequest, ActionClosePullRequest, ActionReopenPullRequest:
 | 
			
		||||
			if !act.Repo.checkUnitUser(e, act.UserID, false, UnitTypePullRequests) {
 | 
			
		||||
				continue
 | 
			
		||||
		// Add feed for organization
 | 
			
		||||
		if act.Repo.Owner.IsOrganization() && act.ActUserID != act.Repo.Owner.ID {
 | 
			
		||||
			act.ID = 0
 | 
			
		||||
			act.UserID = act.Repo.Owner.ID
 | 
			
		||||
			if _, err = e.InsertOne(act); err != nil {
 | 
			
		||||
				return fmt.Errorf("insert new actioner: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if _, err = e.InsertOne(act); err != nil {
 | 
			
		||||
			return fmt.Errorf("insert new action: %v", err)
 | 
			
		||||
		if repoChanged {
 | 
			
		||||
			permCode = make([]bool, len(watchers))
 | 
			
		||||
			permIssue = make([]bool, len(watchers))
 | 
			
		||||
			permPR = make([]bool, len(watchers))
 | 
			
		||||
			for i, watcher := range watchers {
 | 
			
		||||
				user, err := getUserByID(e, watcher.UserID)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					permCode[i] = false
 | 
			
		||||
					permIssue[i] = false
 | 
			
		||||
					permPR[i] = false
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				perm, err := getUserRepoPermission(e, repo, user)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					permCode[i] = false
 | 
			
		||||
					permIssue[i] = false
 | 
			
		||||
					permPR[i] = false
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				permCode[i] = perm.CanRead(UnitTypeCode)
 | 
			
		||||
				permIssue[i] = perm.CanRead(UnitTypeIssues)
 | 
			
		||||
				permPR[i] = perm.CanRead(UnitTypePullRequests)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for i, watcher := range watchers {
 | 
			
		||||
			if act.ActUserID == watcher.UserID {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			act.ID = 0
 | 
			
		||||
			act.UserID = watcher.UserID
 | 
			
		||||
			act.Repo.Units = nil
 | 
			
		||||
 | 
			
		||||
			switch act.OpType {
 | 
			
		||||
			case ActionCommitRepo, ActionPushTag, ActionDeleteTag, ActionDeleteBranch:
 | 
			
		||||
				if !permCode[i] {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
			case ActionCreateIssue, ActionCommentIssue, ActionCloseIssue, ActionReopenIssue:
 | 
			
		||||
				if !permIssue[i] {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
			case ActionCreatePullRequest, ActionCommentPull, ActionMergePullRequest, ActionClosePullRequest, ActionReopenPullRequest:
 | 
			
		||||
				if !permPR[i] {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if _, err = e.InsertOne(act); err != nil {
 | 
			
		||||
				return fmt.Errorf("insert new action: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NotifyWatchers creates batch of actions for every watcher.
 | 
			
		||||
func NotifyWatchers(act *Action) error {
 | 
			
		||||
	return notifyWatchers(x, act)
 | 
			
		||||
func NotifyWatchers(actions ...*Action) error {
 | 
			
		||||
	return notifyWatchers(x, actions...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NotifyWatchersActions creates batch of actions for every watcher.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										179
									
								
								models/update.go
									
										
									
									
									
								
							
							
						
						
									
										179
									
								
								models/update.go
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -53,6 +53,66 @@ func ListToPushCommits(l *list.List) *PushCommits {
 | 
			
		|||
	return &PushCommits{l.Len(), commits, "", make(map[string]string), make(map[string]*User)}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PushUpdateAddDeleteTags updates a number of added and delete tags
 | 
			
		||||
func PushUpdateAddDeleteTags(repo *Repository, gitRepo *git.Repository, addTags, delTags []string) error {
 | 
			
		||||
	sess := x.NewSession()
 | 
			
		||||
	defer sess.Close()
 | 
			
		||||
	if err := sess.Begin(); err != nil {
 | 
			
		||||
		return fmt.Errorf("Unable to begin sess in PushUpdateDeleteTags: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if err := pushUpdateDeleteTags(sess, repo, delTags); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := pushUpdateAddTags(sess, repo, gitRepo, addTags); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return sess.Commit()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PushUpdateDeleteTags updates a number of delete tags
 | 
			
		||||
func PushUpdateDeleteTags(repo *Repository, tags []string) error {
 | 
			
		||||
	sess := x.NewSession()
 | 
			
		||||
	defer sess.Close()
 | 
			
		||||
	if err := sess.Begin(); err != nil {
 | 
			
		||||
		return fmt.Errorf("Unable to begin sess in PushUpdateDeleteTags: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if err := pushUpdateDeleteTags(sess, repo, tags); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return sess.Commit()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func pushUpdateDeleteTags(e Engine, repo *Repository, tags []string) error {
 | 
			
		||||
	if len(tags) == 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	lowerTags := make([]string, 0, len(tags))
 | 
			
		||||
	for _, tag := range tags {
 | 
			
		||||
		lowerTags = append(lowerTags, strings.ToLower(tag))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, err := e.
 | 
			
		||||
		Where("repo_id = ? AND is_tag = ?", repo.ID, true).
 | 
			
		||||
		In("lower_tag_name", lowerTags).
 | 
			
		||||
		Delete(new(Release)); err != nil {
 | 
			
		||||
		return fmt.Errorf("Delete: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, err := e.
 | 
			
		||||
		Where("repo_id = ? AND is_tag = ?", repo.ID, false).
 | 
			
		||||
		In("lower_tag_name", lowerTags).
 | 
			
		||||
		SetExpr("is_draft", true).
 | 
			
		||||
		SetExpr("num_commits", 0).
 | 
			
		||||
		SetExpr("sha1", "").
 | 
			
		||||
		Update(new(Release)); err != nil {
 | 
			
		||||
		return fmt.Errorf("Update: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PushUpdateDeleteTag must be called for any push actions to delete tag
 | 
			
		||||
func PushUpdateDeleteTag(repo *Repository, tagName string) error {
 | 
			
		||||
	rel, err := GetRelease(repo.ID, tagName)
 | 
			
		||||
| 
						 | 
				
			
			@ -78,6 +138,125 @@ func PushUpdateDeleteTag(repo *Repository, tagName string) error {
 | 
			
		|||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PushUpdateAddTags updates a number of add tags
 | 
			
		||||
func PushUpdateAddTags(repo *Repository, gitRepo *git.Repository, tags []string) error {
 | 
			
		||||
	sess := x.NewSession()
 | 
			
		||||
	defer sess.Close()
 | 
			
		||||
	if err := sess.Begin(); err != nil {
 | 
			
		||||
		return fmt.Errorf("Unable to begin sess in PushUpdateAddTags: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if err := pushUpdateAddTags(sess, repo, gitRepo, tags); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return sess.Commit()
 | 
			
		||||
}
 | 
			
		||||
func pushUpdateAddTags(e Engine, repo *Repository, gitRepo *git.Repository, tags []string) error {
 | 
			
		||||
	if len(tags) == 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	lowerTags := make([]string, 0, len(tags))
 | 
			
		||||
	for _, tag := range tags {
 | 
			
		||||
		lowerTags = append(lowerTags, strings.ToLower(tag))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	releases := make([]Release, 0, len(tags))
 | 
			
		||||
	if err := e.Where("repo_id = ?", repo.ID).
 | 
			
		||||
		In("lower_tag_name", lowerTags).Find(&releases); err != nil {
 | 
			
		||||
		return fmt.Errorf("GetRelease: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	relMap := make(map[string]*Release)
 | 
			
		||||
	for _, rel := range releases {
 | 
			
		||||
		relMap[rel.LowerTagName] = &rel
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	newReleases := make([]*Release, 0, len(lowerTags)-len(relMap))
 | 
			
		||||
 | 
			
		||||
	emailToUser := make(map[string]*User)
 | 
			
		||||
 | 
			
		||||
	for i, lowerTag := range lowerTags {
 | 
			
		||||
		tag, err := gitRepo.GetTag(tags[i])
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("GetTag: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		commit, err := tag.Commit()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("Commit: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		sig := tag.Tagger
 | 
			
		||||
		if sig == nil {
 | 
			
		||||
			sig = commit.Author
 | 
			
		||||
		}
 | 
			
		||||
		if sig == nil {
 | 
			
		||||
			sig = commit.Committer
 | 
			
		||||
		}
 | 
			
		||||
		var author *User
 | 
			
		||||
		var createdAt = time.Unix(1, 0)
 | 
			
		||||
 | 
			
		||||
		if sig != nil {
 | 
			
		||||
			var ok bool
 | 
			
		||||
			author, ok = emailToUser[sig.Email]
 | 
			
		||||
			if !ok {
 | 
			
		||||
				author, err = GetUserByEmail(sig.Email)
 | 
			
		||||
				if err != nil && !IsErrUserNotExist(err) {
 | 
			
		||||
					return fmt.Errorf("GetUserByEmail: %v", err)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			createdAt = sig.When
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		commitsCount, err := commit.CommitsCount()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("CommitsCount: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		rel, has := relMap[lowerTag]
 | 
			
		||||
 | 
			
		||||
		if !has {
 | 
			
		||||
			rel = &Release{
 | 
			
		||||
				RepoID:       repo.ID,
 | 
			
		||||
				Title:        "",
 | 
			
		||||
				TagName:      tags[i],
 | 
			
		||||
				LowerTagName: lowerTag,
 | 
			
		||||
				Target:       "",
 | 
			
		||||
				Sha1:         commit.ID.String(),
 | 
			
		||||
				NumCommits:   commitsCount,
 | 
			
		||||
				Note:         "",
 | 
			
		||||
				IsDraft:      false,
 | 
			
		||||
				IsPrerelease: false,
 | 
			
		||||
				IsTag:        true,
 | 
			
		||||
				CreatedUnix:  timeutil.TimeStamp(createdAt.Unix()),
 | 
			
		||||
			}
 | 
			
		||||
			if author != nil {
 | 
			
		||||
				rel.PublisherID = author.ID
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			newReleases = append(newReleases, rel)
 | 
			
		||||
		} else {
 | 
			
		||||
			rel.Sha1 = commit.ID.String()
 | 
			
		||||
			rel.CreatedUnix = timeutil.TimeStamp(createdAt.Unix())
 | 
			
		||||
			rel.NumCommits = commitsCount
 | 
			
		||||
			rel.IsDraft = false
 | 
			
		||||
			if rel.IsTag && author != nil {
 | 
			
		||||
				rel.PublisherID = author.ID
 | 
			
		||||
			}
 | 
			
		||||
			if _, err = e.ID(rel.ID).AllCols().Update(rel); err != nil {
 | 
			
		||||
				return fmt.Errorf("Update: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(newReleases) > 0 {
 | 
			
		||||
		if _, err := e.Insert(newReleases); err != nil {
 | 
			
		||||
			return fmt.Errorf("Insert: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PushUpdateAddTag must be called for any push actions to add tag
 | 
			
		||||
func PushUpdateAddTag(repo *Repository, gitRepo *git.Repository, tagName string) error {
 | 
			
		||||
	rel, err := GetRelease(repo.ID, tagName)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,6 +9,7 @@ import (
 | 
			
		|||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"code.gitea.io/gitea/modules/setting"
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -22,9 +23,9 @@ const (
 | 
			
		|||
 | 
			
		||||
// HookOptions represents the options for the Hook calls
 | 
			
		||||
type HookOptions struct {
 | 
			
		||||
	OldCommitID                     string
 | 
			
		||||
	NewCommitID                     string
 | 
			
		||||
	RefFullName                     string
 | 
			
		||||
	OldCommitIDs                    []string
 | 
			
		||||
	NewCommitIDs                    []string
 | 
			
		||||
	RefFullNames                    []string
 | 
			
		||||
	UserID                          int64
 | 
			
		||||
	UserName                        string
 | 
			
		||||
	GitObjectDirectory              string
 | 
			
		||||
| 
						 | 
				
			
			@ -34,23 +35,33 @@ type HookOptions struct {
 | 
			
		|||
	IsDeployKey                     bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HookPostReceiveResult represents an individual result from PostReceive
 | 
			
		||||
type HookPostReceiveResult struct {
 | 
			
		||||
	Results      []HookPostReceiveBranchResult
 | 
			
		||||
	RepoWasEmpty bool
 | 
			
		||||
	Err          string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HookPostReceiveBranchResult represents an individual branch result from PostReceive
 | 
			
		||||
type HookPostReceiveBranchResult struct {
 | 
			
		||||
	Message bool
 | 
			
		||||
	Create  bool
 | 
			
		||||
	Branch  string
 | 
			
		||||
	URL     string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HookPreReceive check whether the provided commits are allowed
 | 
			
		||||
func HookPreReceive(ownerName, repoName string, opts HookOptions) (int, string) {
 | 
			
		||||
	reqURL := setting.LocalURL + fmt.Sprintf("api/internal/hook/pre-receive/%s/%s?old=%s&new=%s&ref=%s&userID=%d&gitObjectDirectory=%s&gitAlternativeObjectDirectories=%s&gitQuarantinePath=%s&prID=%d&isDeployKey=%t",
 | 
			
		||||
	reqURL := setting.LocalURL + fmt.Sprintf("api/internal/hook/pre-receive/%s/%s",
 | 
			
		||||
		url.PathEscape(ownerName),
 | 
			
		||||
		url.PathEscape(repoName),
 | 
			
		||||
		url.QueryEscape(opts.OldCommitID),
 | 
			
		||||
		url.QueryEscape(opts.NewCommitID),
 | 
			
		||||
		url.QueryEscape(opts.RefFullName),
 | 
			
		||||
		opts.UserID,
 | 
			
		||||
		url.QueryEscape(opts.GitObjectDirectory),
 | 
			
		||||
		url.QueryEscape(opts.GitAlternativeObjectDirectories),
 | 
			
		||||
		url.QueryEscape(opts.GitQuarantinePath),
 | 
			
		||||
		opts.ProtectedBranchID,
 | 
			
		||||
		opts.IsDeployKey,
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	resp, err := newInternalRequest(reqURL, "GET").Response()
 | 
			
		||||
	req := newInternalRequest(reqURL, "POST")
 | 
			
		||||
	req = req.Header("Content-Type", "application/json")
 | 
			
		||||
	jsonBytes, _ := json.Marshal(opts)
 | 
			
		||||
	req.Body(jsonBytes)
 | 
			
		||||
	req.SetTimeout(60*time.Second, time.Duration(60+len(opts.OldCommitIDs))*time.Second)
 | 
			
		||||
	resp, err := req.Response()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return http.StatusInternalServerError, fmt.Sprintf("Unable to contact gitea: %v", err.Error())
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -64,17 +75,18 @@ func HookPreReceive(ownerName, repoName string, opts HookOptions) (int, string)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// HookPostReceive updates services and users
 | 
			
		||||
func HookPostReceive(ownerName, repoName string, opts HookOptions) (map[string]interface{}, string) {
 | 
			
		||||
	reqURL := setting.LocalURL + fmt.Sprintf("api/internal/hook/post-receive/%s/%s?old=%s&new=%s&ref=%s&userID=%d&username=%s",
 | 
			
		||||
func HookPostReceive(ownerName, repoName string, opts HookOptions) (*HookPostReceiveResult, string) {
 | 
			
		||||
	reqURL := setting.LocalURL + fmt.Sprintf("api/internal/hook/post-receive/%s/%s",
 | 
			
		||||
		url.PathEscape(ownerName),
 | 
			
		||||
		url.PathEscape(repoName),
 | 
			
		||||
		url.QueryEscape(opts.OldCommitID),
 | 
			
		||||
		url.QueryEscape(opts.NewCommitID),
 | 
			
		||||
		url.QueryEscape(opts.RefFullName),
 | 
			
		||||
		opts.UserID,
 | 
			
		||||
		url.QueryEscape(opts.UserName))
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	resp, err := newInternalRequest(reqURL, "GET").Response()
 | 
			
		||||
	req := newInternalRequest(reqURL, "POST")
 | 
			
		||||
	req = req.Header("Content-Type", "application/json")
 | 
			
		||||
	req.SetTimeout(60*time.Second, time.Duration(60+len(opts.OldCommitIDs))*time.Second)
 | 
			
		||||
	jsonBytes, _ := json.Marshal(opts)
 | 
			
		||||
	req.Body(jsonBytes)
 | 
			
		||||
	resp, err := req.Response()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Sprintf("Unable to contact gitea: %v", err.Error())
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -83,8 +95,30 @@ func HookPostReceive(ownerName, repoName string, opts HookOptions) (map[string]i
 | 
			
		|||
	if resp.StatusCode != http.StatusOK {
 | 
			
		||||
		return nil, decodeJSONError(resp).Err
 | 
			
		||||
	}
 | 
			
		||||
	res := map[string]interface{}{}
 | 
			
		||||
	_ = json.NewDecoder(resp.Body).Decode(&res)
 | 
			
		||||
	res := &HookPostReceiveResult{}
 | 
			
		||||
	_ = json.NewDecoder(resp.Body).Decode(res)
 | 
			
		||||
 | 
			
		||||
	return res, ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetDefaultBranch will set the default branch to the provided branch for the provided repository
 | 
			
		||||
func SetDefaultBranch(ownerName, repoName, branch string) error {
 | 
			
		||||
	reqURL := setting.LocalURL + fmt.Sprintf("api/internal/hook/set-default-branch/%s/%s/%s",
 | 
			
		||||
		url.PathEscape(ownerName),
 | 
			
		||||
		url.PathEscape(repoName),
 | 
			
		||||
		url.PathEscape(branch),
 | 
			
		||||
	)
 | 
			
		||||
	req := newInternalRequest(reqURL, "POST")
 | 
			
		||||
	req = req.Header("Content-Type", "application/json")
 | 
			
		||||
 | 
			
		||||
	req.SetTimeout(60*time.Second, 60*time.Second)
 | 
			
		||||
	resp, err := req.Response()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Unable to contact gitea: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	defer resp.Body.Close()
 | 
			
		||||
	if resp.StatusCode != http.StatusOK {
 | 
			
		||||
		return fmt.Errorf("Error returned from gitea: %v", decodeJSONError(resp).Err)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -159,112 +159,132 @@ type CommitRepoActionOptions struct {
 | 
			
		|||
 | 
			
		||||
// CommitRepoAction adds new commit action to the repository, and prepare
 | 
			
		||||
// corresponding webhooks.
 | 
			
		||||
func CommitRepoAction(opts CommitRepoActionOptions) error {
 | 
			
		||||
	pusher, err := models.GetUserByName(opts.PusherName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("GetUserByName [%s]: %v", opts.PusherName, err)
 | 
			
		||||
	}
 | 
			
		||||
func CommitRepoAction(optsList ...*CommitRepoActionOptions) error {
 | 
			
		||||
	var pusher *models.User
 | 
			
		||||
	var repo *models.Repository
 | 
			
		||||
	actions := make([]*models.Action, len(optsList))
 | 
			
		||||
 | 
			
		||||
	repo, err := models.GetRepositoryByName(opts.RepoOwnerID, opts.RepoName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("GetRepositoryByName [owner_id: %d, name: %s]: %v", opts.RepoOwnerID, opts.RepoName, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	refName := git.RefEndName(opts.RefFullName)
 | 
			
		||||
 | 
			
		||||
	// Change default branch and empty status only if pushed ref is non-empty branch.
 | 
			
		||||
	if repo.IsEmpty && opts.NewCommitID != git.EmptySHA && strings.HasPrefix(opts.RefFullName, git.BranchPrefix) {
 | 
			
		||||
		repo.DefaultBranch = refName
 | 
			
		||||
		repo.IsEmpty = false
 | 
			
		||||
		if refName != "master" {
 | 
			
		||||
			gitRepo, err := git.OpenRepository(repo.RepoPath())
 | 
			
		||||
	for i, opts := range optsList {
 | 
			
		||||
		if pusher == nil || pusher.Name != opts.PusherName {
 | 
			
		||||
			var err error
 | 
			
		||||
			pusher, err = models.GetUserByName(opts.PusherName)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
				return fmt.Errorf("GetUserByName [%s]: %v", opts.PusherName, err)
 | 
			
		||||
			}
 | 
			
		||||
			if err := gitRepo.SetDefaultBranch(repo.DefaultBranch); err != nil {
 | 
			
		||||
				if !git.IsErrUnsupportedVersion(err) {
 | 
			
		||||
					gitRepo.Close()
 | 
			
		||||
					return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if repo == nil || repo.OwnerID != opts.RepoOwnerID || repo.Name != opts.RepoName {
 | 
			
		||||
			var err error
 | 
			
		||||
			if repo != nil {
 | 
			
		||||
				// Change repository empty status and update last updated time.
 | 
			
		||||
				if err := models.UpdateRepository(repo, false); err != nil {
 | 
			
		||||
					return fmt.Errorf("UpdateRepository: %v", err)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			gitRepo.Close()
 | 
			
		||||
			repo, err = models.GetRepositoryByName(opts.RepoOwnerID, opts.RepoName)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return fmt.Errorf("GetRepositoryByName [owner_id: %d, name: %s]: %v", opts.RepoOwnerID, opts.RepoName, err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
		refName := git.RefEndName(opts.RefFullName)
 | 
			
		||||
 | 
			
		||||
	// Change repository empty status and update last updated time.
 | 
			
		||||
	if err = models.UpdateRepository(repo, false); err != nil {
 | 
			
		||||
		return fmt.Errorf("UpdateRepository: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	isNewBranch := false
 | 
			
		||||
	opType := models.ActionCommitRepo
 | 
			
		||||
	// Check it's tag push or branch.
 | 
			
		||||
	if strings.HasPrefix(opts.RefFullName, git.TagPrefix) {
 | 
			
		||||
		opType = models.ActionPushTag
 | 
			
		||||
		if opts.NewCommitID == git.EmptySHA {
 | 
			
		||||
			opType = models.ActionDeleteTag
 | 
			
		||||
		// Change default branch and empty status only if pushed ref is non-empty branch.
 | 
			
		||||
		if repo.IsEmpty && opts.NewCommitID != git.EmptySHA && strings.HasPrefix(opts.RefFullName, git.BranchPrefix) {
 | 
			
		||||
			repo.DefaultBranch = refName
 | 
			
		||||
			repo.IsEmpty = false
 | 
			
		||||
			if refName != "master" {
 | 
			
		||||
				gitRepo, err := git.OpenRepository(repo.RepoPath())
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
				if err := gitRepo.SetDefaultBranch(repo.DefaultBranch); err != nil {
 | 
			
		||||
					if !git.IsErrUnsupportedVersion(err) {
 | 
			
		||||
						gitRepo.Close()
 | 
			
		||||
						return err
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				gitRepo.Close()
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		opts.Commits = &models.PushCommits{}
 | 
			
		||||
	} else if opts.NewCommitID == git.EmptySHA {
 | 
			
		||||
		opType = models.ActionDeleteBranch
 | 
			
		||||
		opts.Commits = &models.PushCommits{}
 | 
			
		||||
	} else {
 | 
			
		||||
		// if not the first commit, set the compare URL.
 | 
			
		||||
		if opts.OldCommitID == git.EmptySHA {
 | 
			
		||||
			isNewBranch = true
 | 
			
		||||
 | 
			
		||||
		isNewBranch := false
 | 
			
		||||
		opType := models.ActionCommitRepo
 | 
			
		||||
 | 
			
		||||
		// Check it's tag push or branch.
 | 
			
		||||
		if strings.HasPrefix(opts.RefFullName, git.TagPrefix) {
 | 
			
		||||
			opType = models.ActionPushTag
 | 
			
		||||
			if opts.NewCommitID == git.EmptySHA {
 | 
			
		||||
				opType = models.ActionDeleteTag
 | 
			
		||||
			}
 | 
			
		||||
			opts.Commits = &models.PushCommits{}
 | 
			
		||||
		} else if opts.NewCommitID == git.EmptySHA {
 | 
			
		||||
			opType = models.ActionDeleteBranch
 | 
			
		||||
			opts.Commits = &models.PushCommits{}
 | 
			
		||||
		} else {
 | 
			
		||||
			opts.Commits.CompareURL = repo.ComposeCompareURL(opts.OldCommitID, opts.NewCommitID)
 | 
			
		||||
			// if not the first commit, set the compare URL.
 | 
			
		||||
			if opts.OldCommitID == git.EmptySHA {
 | 
			
		||||
				isNewBranch = true
 | 
			
		||||
			} else {
 | 
			
		||||
				opts.Commits.CompareURL = repo.ComposeCompareURL(opts.OldCommitID, opts.NewCommitID)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if err := UpdateIssuesCommit(pusher, repo, opts.Commits.Commits, refName); err != nil {
 | 
			
		||||
				log.Error("updateIssuesCommit: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err = UpdateIssuesCommit(pusher, repo, opts.Commits.Commits, refName); err != nil {
 | 
			
		||||
			log.Error("updateIssuesCommit: %v", err)
 | 
			
		||||
		if len(opts.Commits.Commits) > setting.UI.FeedMaxCommitNum {
 | 
			
		||||
			opts.Commits.Commits = opts.Commits.Commits[:setting.UI.FeedMaxCommitNum]
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		data, err := json.Marshal(opts.Commits)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("Marshal: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		actions[i] = &models.Action{
 | 
			
		||||
			ActUserID: pusher.ID,
 | 
			
		||||
			ActUser:   pusher,
 | 
			
		||||
			OpType:    opType,
 | 
			
		||||
			Content:   string(data),
 | 
			
		||||
			RepoID:    repo.ID,
 | 
			
		||||
			Repo:      repo,
 | 
			
		||||
			RefName:   refName,
 | 
			
		||||
			IsPrivate: repo.IsPrivate,
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var isHookEventPush = true
 | 
			
		||||
		switch opType {
 | 
			
		||||
		case models.ActionCommitRepo: // Push
 | 
			
		||||
			if isNewBranch {
 | 
			
		||||
				notification.NotifyCreateRef(pusher, repo, "branch", opts.RefFullName)
 | 
			
		||||
			}
 | 
			
		||||
		case models.ActionDeleteBranch: // Delete Branch
 | 
			
		||||
			notification.NotifyDeleteRef(pusher, repo, "branch", opts.RefFullName)
 | 
			
		||||
 | 
			
		||||
		case models.ActionPushTag: // Create
 | 
			
		||||
			notification.NotifyCreateRef(pusher, repo, "tag", opts.RefFullName)
 | 
			
		||||
 | 
			
		||||
		case models.ActionDeleteTag: // Delete Tag
 | 
			
		||||
			notification.NotifyDeleteRef(pusher, repo, "tag", opts.RefFullName)
 | 
			
		||||
		default:
 | 
			
		||||
			isHookEventPush = false
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if isHookEventPush {
 | 
			
		||||
			notification.NotifyPushCommits(pusher, repo, opts.RefFullName, opts.OldCommitID, opts.NewCommitID, opts.Commits)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(opts.Commits.Commits) > setting.UI.FeedMaxCommitNum {
 | 
			
		||||
		opts.Commits.Commits = opts.Commits.Commits[:setting.UI.FeedMaxCommitNum]
 | 
			
		||||
	if repo != nil {
 | 
			
		||||
		// Change repository empty status and update last updated time.
 | 
			
		||||
		if err := models.UpdateRepository(repo, false); err != nil {
 | 
			
		||||
			return fmt.Errorf("UpdateRepository: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	data, err := json.Marshal(opts.Commits)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Marshal: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = models.NotifyWatchers(&models.Action{
 | 
			
		||||
		ActUserID: pusher.ID,
 | 
			
		||||
		ActUser:   pusher,
 | 
			
		||||
		OpType:    opType,
 | 
			
		||||
		Content:   string(data),
 | 
			
		||||
		RepoID:    repo.ID,
 | 
			
		||||
		Repo:      repo,
 | 
			
		||||
		RefName:   refName,
 | 
			
		||||
		IsPrivate: repo.IsPrivate,
 | 
			
		||||
	}); err != nil {
 | 
			
		||||
	if err := models.NotifyWatchers(actions...); err != nil {
 | 
			
		||||
		return fmt.Errorf("NotifyWatchers: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var isHookEventPush = true
 | 
			
		||||
	switch opType {
 | 
			
		||||
	case models.ActionCommitRepo: // Push
 | 
			
		||||
		if isNewBranch {
 | 
			
		||||
			notification.NotifyCreateRef(pusher, repo, "branch", opts.RefFullName)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	case models.ActionDeleteBranch: // Delete Branch
 | 
			
		||||
		notification.NotifyDeleteRef(pusher, repo, "branch", opts.RefFullName)
 | 
			
		||||
 | 
			
		||||
	case models.ActionPushTag: // Create
 | 
			
		||||
		notification.NotifyCreateRef(pusher, repo, "tag", opts.RefFullName)
 | 
			
		||||
 | 
			
		||||
	case models.ActionDeleteTag: // Delete Tag
 | 
			
		||||
		notification.NotifyDeleteRef(pusher, repo, "tag", opts.RefFullName)
 | 
			
		||||
	default:
 | 
			
		||||
		isHookEventPush = false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if isHookEventPush {
 | 
			
		||||
		notification.NotifyPushCommits(pusher, repo, opts.RefFullName, opts.OldCommitID, opts.NewCommitID, opts.Commits)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,7 +13,7 @@ import (
 | 
			
		|||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func testCorrectRepoAction(t *testing.T, opts CommitRepoActionOptions, actionBean *models.Action) {
 | 
			
		||||
func testCorrectRepoAction(t *testing.T, opts *CommitRepoActionOptions, actionBean *models.Action) {
 | 
			
		||||
	models.AssertNotExistsBean(t, actionBean)
 | 
			
		||||
	assert.NoError(t, CommitRepoAction(opts))
 | 
			
		||||
	models.AssertExistsAndLoadBean(t, actionBean)
 | 
			
		||||
| 
						 | 
				
			
			@ -121,7 +121,7 @@ func TestCommitRepoAction(t *testing.T) {
 | 
			
		|||
		s.action.Repo = repo
 | 
			
		||||
		s.action.IsPrivate = repo.IsPrivate
 | 
			
		||||
 | 
			
		||||
		testCorrectRepoAction(t, s.commitRepoActionOptions, &s.action)
 | 
			
		||||
		testCorrectRepoAction(t, &s.commitRepoActionOptions, &s.action)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -432,6 +432,7 @@ type PushUpdateOptions struct {
 | 
			
		|||
	RefFullName  string
 | 
			
		||||
	OldCommitID  string
 | 
			
		||||
	NewCommitID  string
 | 
			
		||||
	Branch       string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PushUpdate must be called for any push actions in order to
 | 
			
		||||
| 
						 | 
				
			
			@ -460,60 +461,12 @@ func PushUpdate(repo *models.Repository, branch string, opts PushUpdateOptions)
 | 
			
		|||
		log.Error("Failed to update size for repository: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var commits = &models.PushCommits{}
 | 
			
		||||
	if strings.HasPrefix(opts.RefFullName, git.TagPrefix) {
 | 
			
		||||
		// If is tag reference
 | 
			
		||||
		tagName := opts.RefFullName[len(git.TagPrefix):]
 | 
			
		||||
		if isDelRef {
 | 
			
		||||
			err = models.PushUpdateDeleteTag(repo, tagName)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return fmt.Errorf("PushUpdateDeleteTag: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			// Clear cache for tag commit count
 | 
			
		||||
			cache.Remove(repo.GetCommitsCountCacheKey(tagName, true))
 | 
			
		||||
			err = models.PushUpdateAddTag(repo, gitRepo, tagName)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return fmt.Errorf("PushUpdateAddTag: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else if !isDelRef {
 | 
			
		||||
		// If is branch reference
 | 
			
		||||
 | 
			
		||||
		// Clear cache for branch commit count
 | 
			
		||||
		cache.Remove(repo.GetCommitsCountCacheKey(opts.RefFullName[len(git.BranchPrefix):], true))
 | 
			
		||||
 | 
			
		||||
		newCommit, err := gitRepo.GetCommit(opts.NewCommitID)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("gitRepo.GetCommit: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Push new branch.
 | 
			
		||||
		var l *list.List
 | 
			
		||||
		if isNewRef {
 | 
			
		||||
			l, err = newCommit.CommitsBeforeLimit(10)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return fmt.Errorf("newCommit.CommitsBeforeLimit: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			l, err = newCommit.CommitsBeforeUntil(opts.OldCommitID)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return fmt.Errorf("newCommit.CommitsBeforeUntil: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		commits = models.ListToPushCommits(l)
 | 
			
		||||
	commitRepoActionOptions, err := createCommitRepoActionOption(repo, gitRepo, &opts)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := CommitRepoAction(CommitRepoActionOptions{
 | 
			
		||||
		PusherName:  opts.PusherName,
 | 
			
		||||
		RepoOwnerID: repo.OwnerID,
 | 
			
		||||
		RepoName:    repo.Name,
 | 
			
		||||
		RefFullName: opts.RefFullName,
 | 
			
		||||
		OldCommitID: opts.OldCommitID,
 | 
			
		||||
		NewCommitID: opts.NewCommitID,
 | 
			
		||||
		Commits:     commits,
 | 
			
		||||
	}); err != nil {
 | 
			
		||||
	if err := CommitRepoAction(commitRepoActionOptions); err != nil {
 | 
			
		||||
		return fmt.Errorf("CommitRepoAction: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -532,3 +485,174 @@ func PushUpdate(repo *models.Repository, branch string, opts PushUpdateOptions)
 | 
			
		|||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PushUpdates generates push action history feeds for push updating multiple refs
 | 
			
		||||
func PushUpdates(repo *models.Repository, optsList []*PushUpdateOptions) error {
 | 
			
		||||
	repoPath := repo.RepoPath()
 | 
			
		||||
	_, err := git.NewCommand("update-server-info").RunInDir(repoPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to call 'git update-server-info': %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	gitRepo, err := git.OpenRepository(repoPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("OpenRepository: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if err = repo.UpdateSize(); err != nil {
 | 
			
		||||
		log.Error("Failed to update size for repository: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	actions, err := createCommitRepoActions(repo, gitRepo, optsList)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := CommitRepoAction(actions...); err != nil {
 | 
			
		||||
		return fmt.Errorf("CommitRepoAction: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var pusher *models.User
 | 
			
		||||
 | 
			
		||||
	for _, opts := range optsList {
 | 
			
		||||
		if pusher == nil || pusher.ID != opts.PusherID {
 | 
			
		||||
			var err error
 | 
			
		||||
			pusher, err = models.GetUserByID(opts.PusherID)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		log.Trace("TriggerTask '%s/%s' by %s", repo.Name, opts.Branch, pusher.Name)
 | 
			
		||||
 | 
			
		||||
		go pull_service.AddTestPullRequestTask(pusher, repo.ID, opts.Branch, true)
 | 
			
		||||
 | 
			
		||||
		if err = models.WatchIfAuto(opts.PusherID, repo.ID, true); err != nil {
 | 
			
		||||
			log.Warn("Fail to perform auto watch on user %v for repo %v: %v", opts.PusherID, repo.ID, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func createCommitRepoActions(repo *models.Repository, gitRepo *git.Repository, optsList []*PushUpdateOptions) ([]*CommitRepoActionOptions, error) {
 | 
			
		||||
	addTags := make([]string, 0, len(optsList))
 | 
			
		||||
	delTags := make([]string, 0, len(optsList))
 | 
			
		||||
	actions := make([]*CommitRepoActionOptions, 0, len(optsList))
 | 
			
		||||
 | 
			
		||||
	for _, opts := range optsList {
 | 
			
		||||
		isNewRef := opts.OldCommitID == git.EmptySHA
 | 
			
		||||
		isDelRef := opts.NewCommitID == git.EmptySHA
 | 
			
		||||
		if isNewRef && isDelRef {
 | 
			
		||||
			return nil, fmt.Errorf("Old and new revisions are both %s", git.EmptySHA)
 | 
			
		||||
		}
 | 
			
		||||
		var commits = &models.PushCommits{}
 | 
			
		||||
		if strings.HasPrefix(opts.RefFullName, git.TagPrefix) {
 | 
			
		||||
			// If is tag reference
 | 
			
		||||
			tagName := opts.RefFullName[len(git.TagPrefix):]
 | 
			
		||||
			if isDelRef {
 | 
			
		||||
				delTags = append(delTags, tagName)
 | 
			
		||||
			} else {
 | 
			
		||||
				cache.Remove(repo.GetCommitsCountCacheKey(tagName, true))
 | 
			
		||||
				addTags = append(addTags, tagName)
 | 
			
		||||
			}
 | 
			
		||||
		} else if !isDelRef {
 | 
			
		||||
			// If is branch reference
 | 
			
		||||
 | 
			
		||||
			// Clear cache for branch commit count
 | 
			
		||||
			cache.Remove(repo.GetCommitsCountCacheKey(opts.RefFullName[len(git.BranchPrefix):], true))
 | 
			
		||||
 | 
			
		||||
			newCommit, err := gitRepo.GetCommit(opts.NewCommitID)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, fmt.Errorf("gitRepo.GetCommit: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Push new branch.
 | 
			
		||||
			var l *list.List
 | 
			
		||||
			if isNewRef {
 | 
			
		||||
				l, err = newCommit.CommitsBeforeLimit(10)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return nil, fmt.Errorf("newCommit.CommitsBeforeLimit: %v", err)
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				l, err = newCommit.CommitsBeforeUntil(opts.OldCommitID)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return nil, fmt.Errorf("newCommit.CommitsBeforeUntil: %v", err)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			commits = models.ListToPushCommits(l)
 | 
			
		||||
		}
 | 
			
		||||
		actions = append(actions, &CommitRepoActionOptions{
 | 
			
		||||
			PusherName:  opts.PusherName,
 | 
			
		||||
			RepoOwnerID: repo.OwnerID,
 | 
			
		||||
			RepoName:    repo.Name,
 | 
			
		||||
			RefFullName: opts.RefFullName,
 | 
			
		||||
			OldCommitID: opts.OldCommitID,
 | 
			
		||||
			NewCommitID: opts.NewCommitID,
 | 
			
		||||
			Commits:     commits,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
	if err := models.PushUpdateAddDeleteTags(repo, gitRepo, addTags, delTags); err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("PushUpdateAddDeleteTags: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	return actions, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func createCommitRepoActionOption(repo *models.Repository, gitRepo *git.Repository, opts *PushUpdateOptions) (*CommitRepoActionOptions, error) {
 | 
			
		||||
	isNewRef := opts.OldCommitID == git.EmptySHA
 | 
			
		||||
	isDelRef := opts.NewCommitID == git.EmptySHA
 | 
			
		||||
	if isNewRef && isDelRef {
 | 
			
		||||
		return nil, fmt.Errorf("Old and new revisions are both %s", git.EmptySHA)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var commits = &models.PushCommits{}
 | 
			
		||||
	if strings.HasPrefix(opts.RefFullName, git.TagPrefix) {
 | 
			
		||||
		// If is tag reference
 | 
			
		||||
		tagName := opts.RefFullName[len(git.TagPrefix):]
 | 
			
		||||
		if isDelRef {
 | 
			
		||||
			if err := models.PushUpdateDeleteTag(repo, tagName); err != nil {
 | 
			
		||||
				return nil, fmt.Errorf("PushUpdateDeleteTag: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			// Clear cache for tag commit count
 | 
			
		||||
			cache.Remove(repo.GetCommitsCountCacheKey(tagName, true))
 | 
			
		||||
			if err := models.PushUpdateAddTag(repo, gitRepo, tagName); err != nil {
 | 
			
		||||
				return nil, fmt.Errorf("PushUpdateAddTag: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else if !isDelRef {
 | 
			
		||||
		// If is branch reference
 | 
			
		||||
 | 
			
		||||
		// Clear cache for branch commit count
 | 
			
		||||
		cache.Remove(repo.GetCommitsCountCacheKey(opts.RefFullName[len(git.BranchPrefix):], true))
 | 
			
		||||
 | 
			
		||||
		newCommit, err := gitRepo.GetCommit(opts.NewCommitID)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, fmt.Errorf("gitRepo.GetCommit: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Push new branch.
 | 
			
		||||
		var l *list.List
 | 
			
		||||
		if isNewRef {
 | 
			
		||||
			l, err = newCommit.CommitsBeforeLimit(10)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, fmt.Errorf("newCommit.CommitsBeforeLimit: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			l, err = newCommit.CommitsBeforeUntil(opts.OldCommitID)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, fmt.Errorf("newCommit.CommitsBeforeUntil: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		commits = models.ListToPushCommits(l)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &CommitRepoActionOptions{
 | 
			
		||||
		PusherName:  opts.PusherName,
 | 
			
		||||
		RepoOwnerID: repo.OwnerID,
 | 
			
		||||
		RepoName:    repo.Name,
 | 
			
		||||
		RefFullName: opts.RefFullName,
 | 
			
		||||
		OldCommitID: opts.OldCommitID,
 | 
			
		||||
		NewCommitID: opts.NewCommitID,
 | 
			
		||||
		Commits:     commits,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -197,11 +197,11 @@ func SyncReleasesWithTags(repo *models.Repository, gitRepo *git.Repository) erro
 | 
			
		|||
			}
 | 
			
		||||
			commitID, err := gitRepo.GetTagCommitID(rel.TagName)
 | 
			
		||||
			if err != nil && !git.IsErrNotExist(err) {
 | 
			
		||||
				return fmt.Errorf("GetTagCommitID: %v", err)
 | 
			
		||||
				return fmt.Errorf("GetTagCommitID: %s: %v", rel.TagName, err)
 | 
			
		||||
			}
 | 
			
		||||
			if git.IsErrNotExist(err) || commitID != rel.Sha1 {
 | 
			
		||||
				if err := models.PushUpdateDeleteTag(repo, rel.TagName); err != nil {
 | 
			
		||||
					return fmt.Errorf("PushUpdateDeleteTag: %v", err)
 | 
			
		||||
					return fmt.Errorf("PushUpdateDeleteTag: %s: %v", rel.TagName, err)
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				existingRelTags[strings.ToLower(rel.TagName)] = struct{}{}
 | 
			
		||||
| 
						 | 
				
			
			@ -215,7 +215,7 @@ func SyncReleasesWithTags(repo *models.Repository, gitRepo *git.Repository) erro
 | 
			
		|||
	for _, tagName := range tags {
 | 
			
		||||
		if _, ok := existingRelTags[strings.ToLower(tagName)]; !ok {
 | 
			
		||||
			if err := models.PushUpdateAddTag(repo, gitRepo, tagName); err != nil {
 | 
			
		||||
				return fmt.Errorf("pushUpdateAddTag: %v", err)
 | 
			
		||||
				return fmt.Errorf("pushUpdateAddTag: %s: %v", tagName, err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,20 +22,9 @@ import (
 | 
			
		|||
)
 | 
			
		||||
 | 
			
		||||
// HookPreReceive checks whether a individual commit is acceptable
 | 
			
		||||
func HookPreReceive(ctx *macaron.Context) {
 | 
			
		||||
func HookPreReceive(ctx *macaron.Context, opts private.HookOptions) {
 | 
			
		||||
	ownerName := ctx.Params(":owner")
 | 
			
		||||
	repoName := ctx.Params(":repo")
 | 
			
		||||
	oldCommitID := ctx.QueryTrim("old")
 | 
			
		||||
	newCommitID := ctx.QueryTrim("new")
 | 
			
		||||
	refFullName := ctx.QueryTrim("ref")
 | 
			
		||||
	userID := ctx.QueryInt64("userID")
 | 
			
		||||
	gitObjectDirectory := ctx.QueryTrim("gitObjectDirectory")
 | 
			
		||||
	gitAlternativeObjectDirectories := ctx.QueryTrim("gitAlternativeObjectDirectories")
 | 
			
		||||
	gitQuarantinePath := ctx.QueryTrim("gitQuarantinePath")
 | 
			
		||||
	prID := ctx.QueryInt64("prID")
 | 
			
		||||
	isDeployKey := ctx.QueryBool("isDeployKey")
 | 
			
		||||
 | 
			
		||||
	branchName := strings.TrimPrefix(refFullName, git.BranchPrefix)
 | 
			
		||||
	repo, err := models.GetRepositoryByOwnerAndName(ownerName, repoName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Error("Unable to get repository: %s/%s Error: %v", ownerName, repoName, err)
 | 
			
		||||
| 
						 | 
				
			
			@ -45,206 +34,304 @@ func HookPreReceive(ctx *macaron.Context) {
 | 
			
		|||
		return
 | 
			
		||||
	}
 | 
			
		||||
	repo.OwnerName = ownerName
 | 
			
		||||
	protectBranch, err := models.GetProtectedBranchBy(repo.ID, branchName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Error("Unable to get protected branch: %s in %-v Error: %v", branchName, repo, err)
 | 
			
		||||
		ctx.JSON(500, map[string]interface{}{
 | 
			
		||||
			"err": err.Error(),
 | 
			
		||||
		})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if protectBranch != nil && protectBranch.IsProtected() {
 | 
			
		||||
		// check and deletion
 | 
			
		||||
		if newCommitID == git.EmptySHA {
 | 
			
		||||
			log.Warn("Forbidden: Branch: %s in %-v is protected from deletion", branchName, repo)
 | 
			
		||||
			ctx.JSON(http.StatusForbidden, map[string]interface{}{
 | 
			
		||||
				"err": fmt.Sprintf("branch %s is protected from deletion", branchName),
 | 
			
		||||
 | 
			
		||||
	for i := range opts.OldCommitIDs {
 | 
			
		||||
		oldCommitID := opts.OldCommitIDs[i]
 | 
			
		||||
		newCommitID := opts.NewCommitIDs[i]
 | 
			
		||||
		refFullName := opts.RefFullNames[i]
 | 
			
		||||
 | 
			
		||||
		branchName := strings.TrimPrefix(refFullName, git.BranchPrefix)
 | 
			
		||||
		protectBranch, err := models.GetProtectedBranchBy(repo.ID, branchName)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Error("Unable to get protected branch: %s in %-v Error: %v", branchName, repo, err)
 | 
			
		||||
			ctx.JSON(500, map[string]interface{}{
 | 
			
		||||
				"err": err.Error(),
 | 
			
		||||
			})
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// detect force push
 | 
			
		||||
		if git.EmptySHA != oldCommitID {
 | 
			
		||||
			env := os.Environ()
 | 
			
		||||
			if gitAlternativeObjectDirectories != "" {
 | 
			
		||||
				env = append(env,
 | 
			
		||||
					private.GitAlternativeObjectDirectories+"="+gitAlternativeObjectDirectories)
 | 
			
		||||
			}
 | 
			
		||||
			if gitObjectDirectory != "" {
 | 
			
		||||
				env = append(env,
 | 
			
		||||
					private.GitObjectDirectory+"="+gitObjectDirectory)
 | 
			
		||||
			}
 | 
			
		||||
			if gitQuarantinePath != "" {
 | 
			
		||||
				env = append(env,
 | 
			
		||||
					private.GitQuarantinePath+"="+gitQuarantinePath)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			output, err := git.NewCommand("rev-list", "--max-count=1", oldCommitID, "^"+newCommitID).RunInDirWithEnv(repo.RepoPath(), env)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Error("Unable to detect force push between: %s and %s in %-v Error: %v", oldCommitID, newCommitID, repo, err)
 | 
			
		||||
				ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
 | 
			
		||||
					"err": fmt.Sprintf("Fail to detect force push: %v", err),
 | 
			
		||||
				})
 | 
			
		||||
				return
 | 
			
		||||
			} else if len(output) > 0 {
 | 
			
		||||
				log.Warn("Forbidden: Branch: %s in %-v is protected from force push", branchName, repo)
 | 
			
		||||
		if protectBranch != nil && protectBranch.IsProtected() {
 | 
			
		||||
			// check and deletion
 | 
			
		||||
			if newCommitID == git.EmptySHA {
 | 
			
		||||
				log.Warn("Forbidden: Branch: %s in %-v is protected from deletion", branchName, repo)
 | 
			
		||||
				ctx.JSON(http.StatusForbidden, map[string]interface{}{
 | 
			
		||||
					"err": fmt.Sprintf("branch %s is protected from force push", branchName),
 | 
			
		||||
				})
 | 
			
		||||
				return
 | 
			
		||||
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		canPush := false
 | 
			
		||||
		if isDeployKey {
 | 
			
		||||
			canPush = protectBranch.CanPush && (!protectBranch.EnableWhitelist || protectBranch.WhitelistDeployKeys)
 | 
			
		||||
		} else {
 | 
			
		||||
			canPush = protectBranch.CanUserPush(userID)
 | 
			
		||||
		}
 | 
			
		||||
		if !canPush && prID > 0 {
 | 
			
		||||
			pr, err := models.GetPullRequestByID(prID)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Error("Unable to get PullRequest %d Error: %v", prID, err)
 | 
			
		||||
				ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
 | 
			
		||||
					"err": fmt.Sprintf("Unable to get PullRequest %d Error: %v", prID, err),
 | 
			
		||||
					"err": fmt.Sprintf("branch %s is protected from deletion", branchName),
 | 
			
		||||
				})
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			if !protectBranch.HasEnoughApprovals(pr) {
 | 
			
		||||
				log.Warn("Forbidden: User %d cannot push to protected branch: %s in %-v and pr #%d does not have enough approvals", userID, branchName, repo, pr.Index)
 | 
			
		||||
 | 
			
		||||
			// detect force push
 | 
			
		||||
			if git.EmptySHA != oldCommitID {
 | 
			
		||||
				env := os.Environ()
 | 
			
		||||
				if opts.GitAlternativeObjectDirectories != "" {
 | 
			
		||||
					env = append(env,
 | 
			
		||||
						private.GitAlternativeObjectDirectories+"="+opts.GitAlternativeObjectDirectories)
 | 
			
		||||
				}
 | 
			
		||||
				if opts.GitObjectDirectory != "" {
 | 
			
		||||
					env = append(env,
 | 
			
		||||
						private.GitObjectDirectory+"="+opts.GitObjectDirectory)
 | 
			
		||||
				}
 | 
			
		||||
				if opts.GitQuarantinePath != "" {
 | 
			
		||||
					env = append(env,
 | 
			
		||||
						private.GitQuarantinePath+"="+opts.GitQuarantinePath)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				output, err := git.NewCommand("rev-list", "--max-count=1", oldCommitID, "^"+newCommitID).RunInDirWithEnv(repo.RepoPath(), env)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					log.Error("Unable to detect force push between: %s and %s in %-v Error: %v", oldCommitID, newCommitID, repo, err)
 | 
			
		||||
					ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
 | 
			
		||||
						"err": fmt.Sprintf("Fail to detect force push: %v", err),
 | 
			
		||||
					})
 | 
			
		||||
					return
 | 
			
		||||
				} else if len(output) > 0 {
 | 
			
		||||
					log.Warn("Forbidden: Branch: %s in %-v is protected from force push", branchName, repo)
 | 
			
		||||
					ctx.JSON(http.StatusForbidden, map[string]interface{}{
 | 
			
		||||
						"err": fmt.Sprintf("branch %s is protected from force push", branchName),
 | 
			
		||||
					})
 | 
			
		||||
					return
 | 
			
		||||
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			canPush := false
 | 
			
		||||
			if opts.IsDeployKey {
 | 
			
		||||
				canPush = protectBranch.CanPush && (!protectBranch.EnableWhitelist || protectBranch.WhitelistDeployKeys)
 | 
			
		||||
			} else {
 | 
			
		||||
				canPush = protectBranch.CanUserPush(opts.UserID)
 | 
			
		||||
			}
 | 
			
		||||
			if !canPush && opts.ProtectedBranchID > 0 {
 | 
			
		||||
				pr, err := models.GetPullRequestByID(opts.ProtectedBranchID)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					log.Error("Unable to get PullRequest %d Error: %v", opts.ProtectedBranchID, err)
 | 
			
		||||
					ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
 | 
			
		||||
						"err": fmt.Sprintf("Unable to get PullRequest %d Error: %v", opts.ProtectedBranchID, err),
 | 
			
		||||
					})
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
				if !protectBranch.HasEnoughApprovals(pr) {
 | 
			
		||||
					log.Warn("Forbidden: User %d cannot push to protected branch: %s in %-v and pr #%d does not have enough approvals", opts.UserID, branchName, repo, pr.Index)
 | 
			
		||||
					ctx.JSON(http.StatusForbidden, map[string]interface{}{
 | 
			
		||||
						"err": fmt.Sprintf("protected branch %s can not be pushed to and pr #%d does not have enough approvals", branchName, opts.ProtectedBranchID),
 | 
			
		||||
					})
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
			} else if !canPush {
 | 
			
		||||
				log.Warn("Forbidden: User %d cannot push to protected branch: %s in %-v", opts.UserID, branchName, repo)
 | 
			
		||||
				ctx.JSON(http.StatusForbidden, map[string]interface{}{
 | 
			
		||||
					"err": fmt.Sprintf("protected branch %s can not be pushed to and pr #%d does not have enough approvals", branchName, prID),
 | 
			
		||||
					"err": fmt.Sprintf("protected branch %s can not be pushed to", branchName),
 | 
			
		||||
				})
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		} else if !canPush {
 | 
			
		||||
			log.Warn("Forbidden: User %d cannot push to protected branch: %s in %-v", userID, branchName, repo)
 | 
			
		||||
			ctx.JSON(http.StatusForbidden, map[string]interface{}{
 | 
			
		||||
				"err": fmt.Sprintf("protected branch %s can not be pushed to", branchName),
 | 
			
		||||
			})
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx.PlainText(http.StatusOK, []byte("ok"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HookPostReceive updates services and users
 | 
			
		||||
func HookPostReceive(ctx *macaron.Context) {
 | 
			
		||||
func HookPostReceive(ctx *macaron.Context, opts private.HookOptions) {
 | 
			
		||||
	ownerName := ctx.Params(":owner")
 | 
			
		||||
	repoName := ctx.Params(":repo")
 | 
			
		||||
	oldCommitID := ctx.Query("old")
 | 
			
		||||
	newCommitID := ctx.Query("new")
 | 
			
		||||
	refFullName := ctx.Query("ref")
 | 
			
		||||
	userID := ctx.QueryInt64("userID")
 | 
			
		||||
	userName := ctx.Query("username")
 | 
			
		||||
 | 
			
		||||
	branch := refFullName
 | 
			
		||||
	if strings.HasPrefix(refFullName, git.BranchPrefix) {
 | 
			
		||||
		branch = strings.TrimPrefix(refFullName, git.BranchPrefix)
 | 
			
		||||
	} else if strings.HasPrefix(refFullName, git.TagPrefix) {
 | 
			
		||||
		branch = strings.TrimPrefix(refFullName, git.TagPrefix)
 | 
			
		||||
	var repo *models.Repository
 | 
			
		||||
	updates := make([]*repofiles.PushUpdateOptions, 0, len(opts.OldCommitIDs))
 | 
			
		||||
	wasEmpty := false
 | 
			
		||||
 | 
			
		||||
	for i := range opts.OldCommitIDs {
 | 
			
		||||
		refFullName := opts.RefFullNames[i]
 | 
			
		||||
		branch := opts.RefFullNames[i]
 | 
			
		||||
		if strings.HasPrefix(branch, git.BranchPrefix) {
 | 
			
		||||
			branch = strings.TrimPrefix(branch, git.BranchPrefix)
 | 
			
		||||
		} else {
 | 
			
		||||
			branch = strings.TrimPrefix(branch, git.TagPrefix)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Only trigger activity updates for changes to branches or
 | 
			
		||||
		// tags.  Updates to other refs (eg, refs/notes, refs/changes,
 | 
			
		||||
		// or other less-standard refs spaces are ignored since there
 | 
			
		||||
		// may be a very large number of them).
 | 
			
		||||
		if strings.HasPrefix(refFullName, git.BranchPrefix) || strings.HasPrefix(refFullName, git.TagPrefix) {
 | 
			
		||||
			if repo == nil {
 | 
			
		||||
				var err error
 | 
			
		||||
				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, private.HookPostReceiveResult{
 | 
			
		||||
						Err: fmt.Sprintf("Failed to get repository: %s/%s Error: %v", ownerName, repoName, err),
 | 
			
		||||
					})
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
				if repo.OwnerName == "" {
 | 
			
		||||
					repo.OwnerName = ownerName
 | 
			
		||||
				}
 | 
			
		||||
				wasEmpty = repo.IsEmpty
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			option := repofiles.PushUpdateOptions{
 | 
			
		||||
				RefFullName:  refFullName,
 | 
			
		||||
				OldCommitID:  opts.OldCommitIDs[i],
 | 
			
		||||
				NewCommitID:  opts.NewCommitIDs[i],
 | 
			
		||||
				Branch:       branch,
 | 
			
		||||
				PusherID:     opts.UserID,
 | 
			
		||||
				PusherName:   opts.UserName,
 | 
			
		||||
				RepoUserName: ownerName,
 | 
			
		||||
				RepoName:     repoName,
 | 
			
		||||
			}
 | 
			
		||||
			updates = append(updates, &option)
 | 
			
		||||
			if repo.IsEmpty && branch == "master" && strings.HasPrefix(refFullName, git.BranchPrefix) {
 | 
			
		||||
				// put the master branch first
 | 
			
		||||
				copy(updates[1:], updates)
 | 
			
		||||
				updates[0] = &option
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Only trigger activity updates for changes to branches or
 | 
			
		||||
	// tags.  Updates to other refs (eg, refs/notes, refs/changes,
 | 
			
		||||
	// or other less-standard refs spaces are ignored since there
 | 
			
		||||
	// may be a very large number of them).
 | 
			
		||||
	if strings.HasPrefix(refFullName, git.BranchPrefix) || strings.HasPrefix(refFullName, git.TagPrefix) {
 | 
			
		||||
		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
 | 
			
		||||
		}
 | 
			
		||||
		if err := repofiles.PushUpdate(repo, branch, repofiles.PushUpdateOptions{
 | 
			
		||||
			RefFullName:  refFullName,
 | 
			
		||||
			OldCommitID:  oldCommitID,
 | 
			
		||||
			NewCommitID:  newCommitID,
 | 
			
		||||
			PusherID:     userID,
 | 
			
		||||
			PusherName:   userName,
 | 
			
		||||
			RepoUserName: ownerName,
 | 
			
		||||
			RepoName:     repoName,
 | 
			
		||||
		}); err != nil {
 | 
			
		||||
			log.Error("Failed to Update: %s/%s Branch: %s Error: %v", ownerName, repoName, branch, err)
 | 
			
		||||
			ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
 | 
			
		||||
				"err": fmt.Sprintf("Failed to Update: %s/%s Branch: %s Error: %v", ownerName, repoName, branch, err),
 | 
			
		||||
	if repo != nil && len(updates) > 0 {
 | 
			
		||||
		if err := repofiles.PushUpdates(repo, updates); err != nil {
 | 
			
		||||
			log.Error("Failed to Update: %s/%s Total Updates: %d", ownerName, repoName, len(updates))
 | 
			
		||||
			for i, update := range updates {
 | 
			
		||||
				log.Error("Failed to Update: %s/%s Update: %d/%d: Branch: %s", ownerName, repoName, i, len(updates), update.Branch)
 | 
			
		||||
			}
 | 
			
		||||
			log.Error("Failed to Update: %s/%s Error: %v", ownerName, repoName, err)
 | 
			
		||||
 | 
			
		||||
			ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{
 | 
			
		||||
				Err: fmt.Sprintf("Failed to Update: %s/%s Error: %v", ownerName, repoName, err),
 | 
			
		||||
			})
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if newCommitID != git.EmptySHA && strings.HasPrefix(refFullName, git.BranchPrefix) {
 | 
			
		||||
		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
 | 
			
		||||
		}
 | 
			
		||||
		repo.OwnerName = ownerName
 | 
			
		||||
	results := make([]private.HookPostReceiveBranchResult, 0, len(opts.OldCommitIDs))
 | 
			
		||||
 | 
			
		||||
		pullRequestAllowed := repo.AllowsPulls()
 | 
			
		||||
		if !pullRequestAllowed {
 | 
			
		||||
			ctx.JSON(http.StatusOK, map[string]interface{}{
 | 
			
		||||
				"message": false,
 | 
			
		||||
			})
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	// We have to reload the repo in case its state is changed above
 | 
			
		||||
	repo = nil
 | 
			
		||||
	var baseRepo *models.Repository
 | 
			
		||||
 | 
			
		||||
		baseRepo := repo
 | 
			
		||||
		if repo.IsFork {
 | 
			
		||||
			if err := repo.GetBaseRepo(); err != nil {
 | 
			
		||||
				log.Error("Failed to get Base Repository of Forked repository: %-v Error: %v", repo, err)
 | 
			
		||||
				ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
 | 
			
		||||
					"err": fmt.Sprintf("Failed to get Base Repository of Forked repository: %-v Error: %v", repo, err),
 | 
			
		||||
	for i := range opts.OldCommitIDs {
 | 
			
		||||
		refFullName := opts.RefFullNames[i]
 | 
			
		||||
		newCommitID := opts.NewCommitIDs[i]
 | 
			
		||||
 | 
			
		||||
		branch := git.RefEndName(opts.RefFullNames[i])
 | 
			
		||||
 | 
			
		||||
		if newCommitID != git.EmptySHA && strings.HasPrefix(refFullName, git.BranchPrefix) {
 | 
			
		||||
			if repo == nil {
 | 
			
		||||
				var err error
 | 
			
		||||
				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, private.HookPostReceiveResult{
 | 
			
		||||
						Err:          fmt.Sprintf("Failed to get repository: %s/%s Error: %v", ownerName, repoName, err),
 | 
			
		||||
						RepoWasEmpty: wasEmpty,
 | 
			
		||||
					})
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
				if repo.OwnerName == "" {
 | 
			
		||||
					repo.OwnerName = ownerName
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if !repo.AllowsPulls() {
 | 
			
		||||
					// We can stop there's no need to go any further
 | 
			
		||||
					ctx.JSON(http.StatusOK, private.HookPostReceiveResult{
 | 
			
		||||
						RepoWasEmpty: wasEmpty,
 | 
			
		||||
					})
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
				baseRepo = repo
 | 
			
		||||
 | 
			
		||||
				if repo.IsFork {
 | 
			
		||||
					if err := repo.GetBaseRepo(); err != nil {
 | 
			
		||||
						log.Error("Failed to get Base Repository of Forked repository: %-v Error: %v", repo, err)
 | 
			
		||||
						ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{
 | 
			
		||||
							Err:          fmt.Sprintf("Failed to get Base Repository of Forked repository: %-v Error: %v", repo, err),
 | 
			
		||||
							RepoWasEmpty: wasEmpty,
 | 
			
		||||
						})
 | 
			
		||||
						return
 | 
			
		||||
					}
 | 
			
		||||
					baseRepo = repo.BaseRepo
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if !repo.IsFork && branch == baseRepo.DefaultBranch {
 | 
			
		||||
				results = append(results, private.HookPostReceiveBranchResult{})
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			pr, err := models.GetUnmergedPullRequest(repo.ID, baseRepo.ID, branch, baseRepo.DefaultBranch)
 | 
			
		||||
			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{
 | 
			
		||||
					Err: fmt.Sprintf(
 | 
			
		||||
						"Failed to get active PR in: %-v Branch: %s to: %-v Branch: %s Error: %v", repo, branch, baseRepo, baseRepo.DefaultBranch, err),
 | 
			
		||||
					RepoWasEmpty: wasEmpty,
 | 
			
		||||
				})
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			baseRepo = repo.BaseRepo
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !repo.IsFork && branch == baseRepo.DefaultBranch {
 | 
			
		||||
			ctx.JSON(http.StatusOK, map[string]interface{}{
 | 
			
		||||
				"message": false,
 | 
			
		||||
			})
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		pr, err := models.GetUnmergedPullRequest(repo.ID, baseRepo.ID, branch, baseRepo.DefaultBranch)
 | 
			
		||||
		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, map[string]interface{}{
 | 
			
		||||
				"err": fmt.Sprintf(
 | 
			
		||||
					"Failed to get active PR in: %-v Branch: %s to: %-v Branch: %s Error: %v", repo, branch, baseRepo, baseRepo.DefaultBranch, err),
 | 
			
		||||
			})
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if pr == nil {
 | 
			
		||||
			if repo.IsFork {
 | 
			
		||||
				branch = fmt.Sprintf("%s:%s", repo.OwnerName, branch)
 | 
			
		||||
			if pr == nil {
 | 
			
		||||
				if repo.IsFork {
 | 
			
		||||
					branch = fmt.Sprintf("%s:%s", repo.OwnerName, branch)
 | 
			
		||||
				}
 | 
			
		||||
				results = append(results, private.HookPostReceiveBranchResult{
 | 
			
		||||
					Message: true,
 | 
			
		||||
					Create:  true,
 | 
			
		||||
					Branch:  branch,
 | 
			
		||||
					URL:     fmt.Sprintf("%s/compare/%s...%s", baseRepo.HTMLURL(), util.PathEscapeSegments(baseRepo.DefaultBranch), util.PathEscapeSegments(branch)),
 | 
			
		||||
				})
 | 
			
		||||
			} else {
 | 
			
		||||
				results = append(results, private.HookPostReceiveBranchResult{
 | 
			
		||||
					Message: true,
 | 
			
		||||
					Create:  false,
 | 
			
		||||
					Branch:  branch,
 | 
			
		||||
					URL:     fmt.Sprintf("%s/pulls/%d", baseRepo.HTMLURL(), pr.Index),
 | 
			
		||||
				})
 | 
			
		||||
			}
 | 
			
		||||
			ctx.JSON(http.StatusOK, map[string]interface{}{
 | 
			
		||||
				"message": true,
 | 
			
		||||
				"create":  true,
 | 
			
		||||
				"branch":  branch,
 | 
			
		||||
				"url":     fmt.Sprintf("%s/compare/%s...%s", baseRepo.HTMLURL(), util.PathEscapeSegments(baseRepo.DefaultBranch), util.PathEscapeSegments(branch)),
 | 
			
		||||
			})
 | 
			
		||||
		} else {
 | 
			
		||||
			ctx.JSON(http.StatusOK, map[string]interface{}{
 | 
			
		||||
				"message": true,
 | 
			
		||||
				"create":  false,
 | 
			
		||||
				"branch":  branch,
 | 
			
		||||
				"url":     fmt.Sprintf("%s/pulls/%d", baseRepo.HTMLURL(), pr.Index),
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	ctx.JSON(http.StatusOK, map[string]interface{}{
 | 
			
		||||
		"message": false,
 | 
			
		||||
	ctx.JSON(http.StatusOK, private.HookPostReceiveResult{
 | 
			
		||||
		Results:      results,
 | 
			
		||||
		RepoWasEmpty: wasEmpty,
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetDefaultBranch updates the default branch
 | 
			
		||||
func SetDefaultBranch(ctx *macaron.Context) {
 | 
			
		||||
	ownerName := ctx.Params(":owner")
 | 
			
		||||
	repoName := ctx.Params(":repo")
 | 
			
		||||
	branch := ctx.Params(":branch")
 | 
			
		||||
	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
 | 
			
		||||
	}
 | 
			
		||||
	if repo.OwnerName == "" {
 | 
			
		||||
		repo.OwnerName = ownerName
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	repo.DefaultBranch = branch
 | 
			
		||||
	gitRepo, err := git.OpenRepository(repo.RepoPath())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
 | 
			
		||||
			"Err": fmt.Sprintf("Failed to get git repository: %s/%s Error: %v", ownerName, repoName, err),
 | 
			
		||||
		})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if err := gitRepo.SetDefaultBranch(repo.DefaultBranch); err != nil {
 | 
			
		||||
		if !git.IsErrUnsupportedVersion(err) {
 | 
			
		||||
			gitRepo.Close()
 | 
			
		||||
			ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
 | 
			
		||||
				"Err": fmt.Sprintf("Unable to set default branch onrepository: %s/%s Error: %v", ownerName, repoName, err),
 | 
			
		||||
			})
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	gitRepo.Close()
 | 
			
		||||
 | 
			
		||||
	if err := repo.UpdateDefaultBranch(); err != nil {
 | 
			
		||||
		ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
 | 
			
		||||
			"Err": fmt.Sprintf("Unable to set default branch onrepository: %s/%s Error: %v", ownerName, repoName, err),
 | 
			
		||||
		})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	ctx.PlainText(200, []byte("success"))
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,8 +10,10 @@ import (
 | 
			
		|||
 | 
			
		||||
	"code.gitea.io/gitea/models"
 | 
			
		||||
	"code.gitea.io/gitea/modules/log"
 | 
			
		||||
	"code.gitea.io/gitea/modules/private"
 | 
			
		||||
	"code.gitea.io/gitea/modules/setting"
 | 
			
		||||
 | 
			
		||||
	"gitea.com/macaron/binding"
 | 
			
		||||
	"gitea.com/macaron/macaron"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -77,11 +79,14 @@ func CheckUnitUser(ctx *macaron.Context) {
 | 
			
		|||
// RegisterRoutes registers all internal APIs routes to web application.
 | 
			
		||||
// These APIs will be invoked by internal commands for example `gitea serv` and etc.
 | 
			
		||||
func RegisterRoutes(m *macaron.Macaron) {
 | 
			
		||||
	bind := binding.Bind
 | 
			
		||||
 | 
			
		||||
	m.Group("/", func() {
 | 
			
		||||
		m.Post("/ssh/authorized_keys", AuthorizedPublicKeyByContent)
 | 
			
		||||
		m.Post("/ssh/:id/update/:repoid", UpdatePublicKeyInRepo)
 | 
			
		||||
		m.Get("/hook/pre-receive/:owner/:repo", HookPreReceive)
 | 
			
		||||
		m.Get("/hook/post-receive/:owner/:repo", HookPostReceive)
 | 
			
		||||
		m.Post("/hook/pre-receive/:owner/:repo", bind(private.HookOptions{}), HookPreReceive)
 | 
			
		||||
		m.Post("/hook/post-receive/:owner/:repo", bind(private.HookOptions{}), HookPostReceive)
 | 
			
		||||
		m.Post("/hook/set-default-branch/:owner/:repo/:branch", SetDefaultBranch)
 | 
			
		||||
		m.Get("/serv/none/:keyid", ServNoCommand)
 | 
			
		||||
		m.Get("/serv/command/:keyid/:owner/:repo", ServCommand)
 | 
			
		||||
	}, CheckInternalToken)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue