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
				
			
		
							
								
								
									
										211
									
								
								cmd/hook.go
									
									
									
									
									
								
							
							
						
						
									
										211
									
								
								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,28 +111,71 @@ 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, | ||||
| 			}) | ||||
| 			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]) | ||||
| 		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() | ||||
| 
 | ||||
| 		res, err := private.HookPostReceive(repoUser, repoName, private.HookOptions{ | ||||
| 			OldCommitID: oldCommitID, | ||||
| 			NewCommitID: newCommitID, | ||||
| 			RefFullName: refFullName, | ||||
| 			UserID:      pusherID, | ||||
| 			UserName:    pusherName, | ||||
| 		}) | ||||
| 
 | ||||
| 		if res == nil { | ||||
| 		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 res["message"] == false { | ||||
| 	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,12 +164,24 @@ func (repo *Repository) GetWatchers(page int) ([]*User, error) { | |||
| 	return users, sess.Find(&users) | ||||
| } | ||||
| 
 | ||||
| func notifyWatchers(e Engine, act *Action) error { | ||||
| 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 | ||||
| 
 | ||||
| 	for _, act := range actions { | ||||
| 		repoChanged := repo == nil || repo.ID != act.RepoID | ||||
| 
 | ||||
| 		if repoChanged { | ||||
| 			// Add feeds for user self and all watchers.
 | ||||
| 	watches, err := getWatchers(e, act.RepoID) | ||||
| 			watchers, err = getWatchers(e, act.RepoID) | ||||
| 			if err != nil { | ||||
| 				return fmt.Errorf("get watchers: %v", err) | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		// Add feed for actioner.
 | ||||
| 		act.UserID = act.ActUserID | ||||
|  | @ -177,11 +189,17 @@ func notifyWatchers(e Engine, act *Action) error { | |||
| 			return fmt.Errorf("insert new actioner: %v", err) | ||||
| 		} | ||||
| 
 | ||||
| 		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 | ||||
| 		} | ||||
| 
 | ||||
| 		// Add feed for organization
 | ||||
| 		if act.Repo.Owner.IsOrganization() && act.ActUserID != act.Repo.Owner.ID { | ||||
|  | @ -192,26 +210,50 @@ func notifyWatchers(e Engine, act *Action) error { | |||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 	for i := range watches { | ||||
| 		if act.ActUserID == watches[i].UserID { | ||||
| 		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 = watches[i].UserID | ||||
| 			act.UserID = watcher.UserID | ||||
| 			act.Repo.Units = nil | ||||
| 
 | ||||
| 			switch act.OpType { | ||||
| 			case ActionCommitRepo, ActionPushTag, ActionDeleteTag, ActionDeleteBranch: | ||||
| 			if !act.Repo.checkUnitUser(e, act.UserID, false, UnitTypeCode) { | ||||
| 				if !permCode[i] { | ||||
| 					continue | ||||
| 				} | ||||
| 			case ActionCreateIssue, ActionCommentIssue, ActionCloseIssue, ActionReopenIssue: | ||||
| 			if !act.Repo.checkUnitUser(e, act.UserID, false, UnitTypeIssues) { | ||||
| 				if !permIssue[i] { | ||||
| 					continue | ||||
| 				} | ||||
| 			case ActionCreatePullRequest, ActionCommentPull, ActionMergePullRequest, ActionClosePullRequest, ActionReopenPullRequest: | ||||
| 			if !act.Repo.checkUnitUser(e, act.UserID, false, UnitTypePullRequests) { | ||||
| 				if !permPR[i] { | ||||
| 					continue | ||||
| 				} | ||||
| 			} | ||||
|  | @ -220,12 +262,13 @@ func notifyWatchers(e Engine, act *Action) error { | |||
| 				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,17 +159,33 @@ 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) | ||||
| func CommitRepoAction(optsList ...*CommitRepoActionOptions) error { | ||||
| 	var pusher *models.User | ||||
| 	var repo *models.Repository | ||||
| 	actions := make([]*models.Action, len(optsList)) | ||||
| 
 | ||||
| 	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 fmt.Errorf("GetUserByName [%s]: %v", opts.PusherName, err) | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 	repo, err := models.GetRepositoryByName(opts.RepoOwnerID, opts.RepoName) | ||||
| 		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) | ||||
| 				} | ||||
| 			} | ||||
| 			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.
 | ||||
|  | @ -191,13 +207,9 @@ func CommitRepoAction(opts CommitRepoActionOptions) error { | |||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 	// 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 | ||||
|  | @ -216,7 +228,7 @@ func CommitRepoAction(opts CommitRepoActionOptions) error { | |||
| 				opts.Commits.CompareURL = repo.ComposeCompareURL(opts.OldCommitID, opts.NewCommitID) | ||||
| 			} | ||||
| 
 | ||||
| 		if err = UpdateIssuesCommit(pusher, repo, opts.Commits.Commits, refName); err != nil { | ||||
| 			if err := UpdateIssuesCommit(pusher, repo, opts.Commits.Commits, refName); err != nil { | ||||
| 				log.Error("updateIssuesCommit: %v", err) | ||||
| 			} | ||||
| 		} | ||||
|  | @ -230,7 +242,7 @@ func CommitRepoAction(opts CommitRepoActionOptions) error { | |||
| 			return fmt.Errorf("Marshal: %v", err) | ||||
| 		} | ||||
| 
 | ||||
| 	if err = models.NotifyWatchers(&models.Action{ | ||||
| 		actions[i] = &models.Action{ | ||||
| 			ActUserID: pusher.ID, | ||||
| 			ActUser:   pusher, | ||||
| 			OpType:    opType, | ||||
|  | @ -239,8 +251,6 @@ func CommitRepoAction(opts CommitRepoActionOptions) error { | |||
| 			Repo:      repo, | ||||
| 			RefName:   refName, | ||||
| 			IsPrivate: repo.IsPrivate, | ||||
| 	}); err != nil { | ||||
| 		return fmt.Errorf("NotifyWatchers: %v", err) | ||||
| 		} | ||||
| 
 | ||||
| 		var isHookEventPush = true | ||||
|  | @ -249,7 +259,6 @@ func CommitRepoAction(opts CommitRepoActionOptions) error { | |||
| 			if isNewBranch { | ||||
| 				notification.NotifyCreateRef(pusher, repo, "branch", opts.RefFullName) | ||||
| 			} | ||||
| 
 | ||||
| 		case models.ActionDeleteBranch: // Delete Branch
 | ||||
| 			notification.NotifyDeleteRef(pusher, repo, "branch", opts.RefFullName) | ||||
| 
 | ||||
|  | @ -265,6 +274,17 @@ func CommitRepoAction(opts CommitRepoActionOptions) error { | |||
| 		if isHookEventPush { | ||||
| 			notification.NotifyPushCommits(pusher, repo, opts.RefFullName, opts.OldCommitID, opts.NewCommitID, opts.Commits) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	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) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if err := models.NotifyWatchers(actions...); err != nil { | ||||
| 		return fmt.Errorf("NotifyWatchers: %v", err) | ||||
| 	} | ||||
| 	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) | ||||
| 	commitRepoActionOptions, err := createCommitRepoActionOption(repo, gitRepo, &opts) | ||||
| 	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) | ||||
| 		return 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) | ||||
| 	} | ||||
| 
 | ||||
| 	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,6 +34,13 @@ func HookPreReceive(ctx *macaron.Context) { | |||
| 		return | ||||
| 	} | ||||
| 	repo.OwnerName = ownerName | ||||
| 
 | ||||
| 	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) | ||||
|  | @ -66,17 +62,17 @@ func HookPreReceive(ctx *macaron.Context) { | |||
| 			// detect force push
 | ||||
| 			if git.EmptySHA != oldCommitID { | ||||
| 				env := os.Environ() | ||||
| 			if gitAlternativeObjectDirectories != "" { | ||||
| 				if opts.GitAlternativeObjectDirectories != "" { | ||||
| 					env = append(env, | ||||
| 					private.GitAlternativeObjectDirectories+"="+gitAlternativeObjectDirectories) | ||||
| 						private.GitAlternativeObjectDirectories+"="+opts.GitAlternativeObjectDirectories) | ||||
| 				} | ||||
| 			if gitObjectDirectory != "" { | ||||
| 				if opts.GitObjectDirectory != "" { | ||||
| 					env = append(env, | ||||
| 					private.GitObjectDirectory+"="+gitObjectDirectory) | ||||
| 						private.GitObjectDirectory+"="+opts.GitObjectDirectory) | ||||
| 				} | ||||
| 			if gitQuarantinePath != "" { | ||||
| 				if opts.GitQuarantinePath != "" { | ||||
| 					env = append(env, | ||||
| 					private.GitQuarantinePath+"="+gitQuarantinePath) | ||||
| 						private.GitQuarantinePath+"="+opts.GitQuarantinePath) | ||||
| 				} | ||||
| 
 | ||||
| 				output, err := git.NewCommand("rev-list", "--max-count=1", oldCommitID, "^"+newCommitID).RunInDirWithEnv(repo.RepoPath(), env) | ||||
|  | @ -95,55 +91,57 @@ func HookPreReceive(ctx *macaron.Context) { | |||
| 
 | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			canPush := false | ||||
| 		if isDeployKey { | ||||
| 			if opts.IsDeployKey { | ||||
| 				canPush = protectBranch.CanPush && (!protectBranch.EnableWhitelist || protectBranch.WhitelistDeployKeys) | ||||
| 			} else { | ||||
| 			canPush = protectBranch.CanUserPush(userID) | ||||
| 				canPush = protectBranch.CanUserPush(opts.UserID) | ||||
| 			} | ||||
| 		if !canPush && prID > 0 { | ||||
| 			pr, err := models.GetPullRequestByID(prID) | ||||
| 			if !canPush && opts.ProtectedBranchID > 0 { | ||||
| 				pr, err := models.GetPullRequestByID(opts.ProtectedBranchID) | ||||
| 				if err != nil { | ||||
| 				log.Error("Unable to get PullRequest %d Error: %v", prID, err) | ||||
| 					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", prID, err), | ||||
| 						"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", userID, branchName, repo, pr.Index) | ||||
| 					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, prID), | ||||
| 						"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", userID, branchName, repo) | ||||
| 				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", 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
 | ||||
|  | @ -151,75 +149,118 @@ func HookPostReceive(ctx *macaron.Context) { | |||
| 		// 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 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, map[string]interface{}{ | ||||
| 				"err": fmt.Sprintf("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 err := repofiles.PushUpdate(repo, branch, repofiles.PushUpdateOptions{ | ||||
| 				if repo.OwnerName == "" { | ||||
| 					repo.OwnerName = ownerName | ||||
| 				} | ||||
| 				wasEmpty = repo.IsEmpty | ||||
| 			} | ||||
| 
 | ||||
| 			option := repofiles.PushUpdateOptions{ | ||||
| 				RefFullName:  refFullName, | ||||
| 			OldCommitID:  oldCommitID, | ||||
| 			NewCommitID:  newCommitID, | ||||
| 			PusherID:     userID, | ||||
| 			PusherName:   userName, | ||||
| 				OldCommitID:  opts.OldCommitIDs[i], | ||||
| 				NewCommitID:  opts.NewCommitIDs[i], | ||||
| 				Branch:       branch, | ||||
| 				PusherID:     opts.UserID, | ||||
| 				PusherName:   opts.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), | ||||
| 			} | ||||
| 			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 | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	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 | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	results := make([]private.HookPostReceiveBranchResult, 0, len(opts.OldCommitIDs)) | ||||
| 
 | ||||
| 	// We have to reload the repo in case its state is changed above
 | ||||
| 	repo = nil | ||||
| 	var baseRepo *models.Repository | ||||
| 
 | ||||
| 	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) { | ||||
| 		repo, err := models.GetRepositoryByOwnerAndName(ownerName, repoName) | ||||
| 			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, map[string]interface{}{ | ||||
| 				"err": fmt.Sprintf("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 | ||||
| 				} | ||||
| 
 | ||||
| 		pullRequestAllowed := repo.AllowsPulls() | ||||
| 		if !pullRequestAllowed { | ||||
| 			ctx.JSON(http.StatusOK, map[string]interface{}{ | ||||
| 				"message": false, | ||||
| 				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 | ||||
| 
 | ||||
| 		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), | ||||
| 						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 { | ||||
| 			ctx.JSON(http.StatusOK, map[string]interface{}{ | ||||
| 				"message": false, | ||||
| 			}) | ||||
| 			return | ||||
| 				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, map[string]interface{}{ | ||||
| 				"err": fmt.Sprintf( | ||||
| 				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 | ||||
| 			} | ||||
|  | @ -228,23 +269,69 @@ func HookPostReceive(ctx *macaron.Context) { | |||
| 				if repo.IsFork { | ||||
| 					branch = fmt.Sprintf("%s:%s", repo.OwnerName, branch) | ||||
| 				} | ||||
| 			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)), | ||||
| 				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 { | ||||
| 			ctx.JSON(http.StatusOK, map[string]interface{}{ | ||||
| 				"message": true, | ||||
| 				"create":  false, | ||||
| 				"branch":  branch, | ||||
| 				"url":     fmt.Sprintf("%s/pulls/%d", baseRepo.HTMLURL(), pr.Index), | ||||
| 				results = append(results, private.HookPostReceiveBranchResult{ | ||||
| 					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…
	
		Reference in a new issue