From 71bc5ab9dc331c84f2f3a6f30095a41efe9548a3 Mon Sep 17 00:00:00 2001 From: a1012112796 <1012112796@qq.com> Date: Tue, 21 Apr 2020 16:34:58 +0800 Subject: [PATCH 01/22] Add push commits history comment on PR time-line * Add notify by email and ui of this comment type also Signed-off-by: a1012112796 <1012112796@qq.com> --- models/issue_comment.go | 198 ++++++++++++++++++ modules/notification/action/action.go | 3 + modules/notification/indexer/indexer.go | 4 + modules/notification/mail/mail.go | 2 + modules/notification/webhook/webhook.go | 4 + options/locale/locale_en-US.ini | 3 + routers/repo/issue.go | 6 + services/pull/pull.go | 38 ++++ templates/repo/commits_list_small.tmpl | 57 +++++ .../repo/issue/view_content/comments.tmpl | 24 ++- web_src/less/_repository.less | 78 +++++++ 11 files changed, 415 insertions(+), 2 deletions(-) create mode 100644 templates/repo/commits_list_small.tmpl diff --git a/models/issue_comment.go b/models/issue_comment.go index f7017435d77d9..11aa9125abf3f 100644 --- a/models/issue_comment.go +++ b/models/issue_comment.go @@ -7,6 +7,7 @@ package models import ( + "container/list" "fmt" "strings" @@ -90,6 +91,8 @@ const ( CommentTypeReviewRequest // merge pull request CommentTypeMergePull + // push to PR head branch + CommentTypePullPush ) // CommentTag defines comment tag type @@ -167,6 +170,11 @@ type Comment struct { RefRepo *Repository `xorm:"-"` RefIssue *Issue `xorm:"-"` RefComment *Comment `xorm:"-"` + + Commits *list.List `xorm:"-"` + OldCommit string `xorm:"-"` + NewCommit string `xorm:"-"` + IsForcePush bool } // LoadIssue loads issue from database @@ -543,6 +551,48 @@ func (c *Comment) CodeCommentURL() string { return fmt.Sprintf("%s/files#%s", c.Issue.HTMLURL(), c.HashTag()) } +// LoadPushCommits Load push refs commits and add participants +func (c *Comment) LoadPushCommits(participants []*User) error { + var err error = nil + if c.Content == "" { + return err + } + + commitIDs := strings.Split(c.Content, ":") + if int64(len(commitIDs)) != c.Line { + return fmt.Errorf("LoadPushCommits: len of commitIDs is wrong %d - %d", + len(commitIDs), + c.Line) + } + c.Commits, err = getCommitsFromCommitIDs(c.Issue.PullRequest.BaseRepo, commitIDs) + if err != nil { + return err + } + + for e := c.Commits.Front(); e != nil; e = e.Next() { + if c.Poster.Email != e.Value.(*git.Commit).Author.Email { + commiter, err := GetUserByEmail(e.Value.(*git.Commit).Author.Email) + if err != nil || commiter == nil { + if IsErrUserNotExist(err) { + err = nil + } + return err + } + participants = append(participants, commiter) + } + } + + c.Commits = ValidateCommitsWithEmails(c.Commits) + c.Commits = ParseCommitsWithSignature(c.Commits, c.Issue.PullRequest.BaseRepo) + c.Commits = ParseCommitsWithStatus(c.Commits, c.Issue.PullRequest.BaseRepo) + + if c.IsForcePush { + c.OldCommit = commitIDs[0] + c.NewCommit = commitIDs[1] + } + return err +} + func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err error) { var LabelID int64 if opts.Label != nil { @@ -576,6 +626,7 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err RefCommentID: opts.RefCommentID, RefAction: opts.RefAction, RefIsPull: opts.RefIsPull, + IsForcePush: opts.IsForcePush, } if _, err = e.Insert(comment); err != nil { return nil, err @@ -738,6 +789,7 @@ type CreateCommentOptions struct { RefCommentID int64 RefAction references.XRefAction RefIsPull bool + IsForcePush bool } // CreateComment creates comment of issue or commit. @@ -1016,3 +1068,149 @@ func UpdateCommentsMigrationsByType(tp structs.GitServiceType, originalAuthorID }) return err } + +// CreatePushPullCommend create push code to pull base commend +func CreatePushPullCommend(pusher *User, pr *PullRequest, oldCommitID, newCommitID string) (comment *Comment, err error) { + ops := &CreateCommentOptions{ + Type: CommentTypePullPush, + Doer: pusher, + Repo: pr.BaseRepo, + } + + var commitIDs []string + messages := "" + + var isForcePush bool + if oldCommitID != "" && newCommitID != "" { + commitIDs, messages, isForcePush, err = getCommitIDsFromRepo(pr.BaseRepo, oldCommitID, newCommitID) + if err != nil { + return nil, err + } + } else { + return nil, nil + } + + ops.LineNum = int64(len(commitIDs)) + ops.Issue = pr.Issue + ops.IsForcePush = isForcePush + + commitIDlist := "" + + for _, commitID := range commitIDs { + commitIDlist = commitID + ":" + commitIDlist + } + + ops.Content = commitIDlist[0 : len(commitIDlist)-1] + + comment, err = CreateComment(ops) + if err != nil { + return + } + + // Prepare the contents for notify + prmessages := "@" + ops.Doer.Name + if ops.IsForcePush { + prmessages += " force-pushed the " + pr.HeadBranch + " branch from " + oldCommitID + " to " + newCommitID + } else { + if ops.LineNum == 1 { + prmessages += fmt.Sprintf(" pushed 1 commit to %s: \n ", pr.HeadBranch) + } else { + prmessages += fmt.Sprintf(" pushed %d commits to %s: \n ", ops.LineNum, pr.HeadBranch) + } + prmessages += messages + } + + comment.Content = prmessages + + return +} + +// getCommitsFromRepo get commit IDs from repo in betwern oldCommitID and newCommitID +// isForcePush will be true if newCommitID is older than oldCommitID +func getCommitIDsFromRepo(repo *Repository, oldCommitID, newCommitID string) (commitIDs []string, messages string, isForcePush bool, err error) { + if oldCommitID == "" || oldCommitID == git.EmptySHA || newCommitID == "" || newCommitID == git.EmptySHA { + return nil, "", false, nil + } + + repoPath := repo.RepoPath() + gitRepo, err := git.OpenRepository(repoPath) + if err != nil { + return nil, "", false, err + } + defer gitRepo.Close() + + newCommit, err := gitRepo.GetCommit(newCommitID) + if err != nil { + return nil, "", false, err + } + + var commits *list.List + commits, err = newCommit.CommitsBeforeUntil(oldCommitID) + if err != nil { + return nil, "", false, err + } + + commitIDs = make([]string, 0, commits.Len()) + messages = "" + + for e := commits.Front(); e != nil; e = e.Next() { + commitID := e.Value.(*git.Commit).ID.String() + commitMsg := e.Value.(*git.Commit).Message() + commitMsgs := strings.Split(commitMsg, "\n") + messages = "* " + commitID[0:10] + " - " + commitMsgs[0] + " \n " + messages + commitIDs = append(commitIDs, commitID) + } + + // check is force push by check the parent of commitIDs[commits.Len()-1] + isForcePush = true + checkCommit, err := gitRepo.GetCommit(commitIDs[commits.Len()-1]) + if err != nil { + return nil, "", false, err + } + + var parentCommit *git.Commit + parentNum := checkCommit.ParentCount() + if parentNum > 0 { + for i := 0; i < parentNum; i++ { + parentCommit, _ = checkCommit.Parent(i) + if parentCommit.ID.String() == oldCommitID { + isForcePush = false + break + } + } + } + + if isForcePush { + commitIDs = make([]string, 2, 2) + commitIDs[1] = oldCommitID + commitIDs[0] = newCommitID + } + + return +} + +// getCommitsFromCommitIDs get commits from commitIDs +func getCommitsFromCommitIDs(repo *Repository, commitIDs []string) (commits *list.List, err error) { + if commitIDs == nil { + return nil, nil + } + + repoPath := repo.RepoPath() + gitRepo, err := git.OpenRepository(repoPath) + if err != nil { + return nil, err + } + defer gitRepo.Close() + + commits = list.New() + + for _, commitID := range commitIDs { + commit, err := gitRepo.GetCommit(commitID) + if err != nil { + return nil, err + } + commits.PushBack(commit) + } + + return commits, nil +} diff --git a/modules/notification/action/action.go b/modules/notification/action/action.go index 9956940f30b22..5bd1f2e0ea99c 100644 --- a/modules/notification/action/action.go +++ b/modules/notification/action/action.go @@ -89,6 +89,9 @@ func (a *actionNotifier) NotifyIssueChangeStatus(doer *models.User, issue *model // NotifyCreateIssueComment notifies comment on an issue to notifiers func (a *actionNotifier) NotifyCreateIssueComment(doer *models.User, repo *models.Repository, issue *models.Issue, comment *models.Comment) { + if comment.Type == models.CommentTypePullPush { + return + } act := &models.Action{ ActUserID: doer.ID, ActUser: doer, diff --git a/modules/notification/indexer/indexer.go b/modules/notification/indexer/indexer.go index 6caae6fa65a12..7fdf65779cfbe 100644 --- a/modules/notification/indexer/indexer.go +++ b/modules/notification/indexer/indexer.go @@ -31,6 +31,10 @@ func NewNotifier() base.Notifier { func (r *indexerNotifier) NotifyCreateIssueComment(doer *models.User, repo *models.Repository, issue *models.Issue, comment *models.Comment) { + if comment.Type == models.CommentTypePullPush { + return + } + if comment.Type == models.CommentTypeComment { if issue.Comments == nil { if err := issue.LoadDiscussComments(); err != nil { diff --git a/modules/notification/mail/mail.go b/modules/notification/mail/mail.go index b5c2db383153c..172e0421a28b6 100644 --- a/modules/notification/mail/mail.go +++ b/modules/notification/mail/mail.go @@ -37,6 +37,8 @@ func (m *mailNotifier) NotifyCreateIssueComment(doer *models.User, repo *models. act = models.ActionCommentIssue } else if comment.Type == models.CommentTypeCode { act = models.ActionCommentIssue + } else if comment.Type == models.CommentTypePullPush { + act = models.ActionCommentIssue } if err := mailer.MailParticipantsComment(comment, act, issue); err != nil { diff --git a/modules/notification/webhook/webhook.go b/modules/notification/webhook/webhook.go index 625cf119a9ac1..962160a251c2d 100644 --- a/modules/notification/webhook/webhook.go +++ b/modules/notification/webhook/webhook.go @@ -392,6 +392,10 @@ func (m *webhookNotifier) NotifyUpdateComment(doer *models.User, c *models.Comme func (m *webhookNotifier) NotifyCreateIssueComment(doer *models.User, repo *models.Repository, issue *models.Issue, comment *models.Comment) { + if comment.Type == models.CommentTypePullPush { + return + } + mode, _ := models.AccessLevel(doer, repo) var err error diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index cfad41ceb54c5..38cb143515da7 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1013,6 +1013,9 @@ issues.due_date = Due Date issues.invalid_due_date_format = "Due date format must be 'yyyy-mm-dd'." issues.error_modifying_due_date = "Failed to modify the due date." issues.error_removing_due_date = "Failed to remove the due date." +issues.push_commit_1 = "add %d commit %s" +issues.push_commits_n = "add %d commits %s" +issues.force_push_codes = `force-pushed the %[1]s branch from %[2]s to %[4]s. %[6]s` issues.due_date_form = "yyyy-mm-dd" issues.due_date_form_add = "Add due date" issues.due_date_form_edit = "Edit" diff --git a/routers/repo/issue.go b/routers/repo/issue.go index a7fda4e7692de..ed49a2dde8838 100644 --- a/routers/repo/issue.go +++ b/routers/repo/issue.go @@ -995,6 +995,12 @@ func ViewIssue(ctx *context.Context) { ctx.ServerError("LoadResolveDoer", err) return } + } else if comment.Type == models.CommentTypePullPush { + participants = addParticipant(comment.Poster, participants) + if err = comment.LoadPushCommits(participants); err != nil { + ctx.ServerError("LoadPushCommits", err) + return + } } } diff --git a/services/pull/pull.go b/services/pull/pull.go index fb4af06372844..593f729941064 100644 --- a/services/pull/pull.go +++ b/services/pull/pull.go @@ -57,6 +57,38 @@ func NewPullRequest(repo *models.Repository, pull *models.Issue, labelIDs []int6 notification.NotifyNewPullRequest(pr) + // add first push codes command + baseGitRepo, err := git.OpenRepository(pr.BaseRepo.RepoPath()) + if err != nil { + return nil // just return , It's not important + } + defer baseGitRepo.Close() + + compareInfo, err := baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), + pr.BaseBranch, pr.GetGitRefName()) + + if compareInfo.Commits.Len() > 0 { + comitIDs := "" + for e := compareInfo.Commits.Front(); e != nil; e = e.Next() { + commitID := e.Value.(*git.Commit).ID.String() + comitIDs = commitID + ":" + comitIDs + } + + comitIDs = comitIDs[0 : len(comitIDs)-1] + + ops := &models.CreateCommentOptions{ + Type: models.CommentTypePullPush, + Doer: pull.Poster, + Repo: repo, + LineNum: int64(compareInfo.Commits.Len()), + Issue: pr.Issue, + RemovedAssignee: false, + Content: comitIDs, + } + + _, _ = models.CreateComment(ops) + } + return nil } @@ -237,6 +269,12 @@ func AddTestPullRequestTask(doer *models.User, repoID int64, branch string, isSy } addHeadRepoTasks(prs) + for _, pr := range prs { + comment, err := models.CreatePushPullCommend(doer, pr, oldCommitID, newCommitID) + if err == nil { + notification.NotifyCreateIssueComment(doer, pr.BaseRepo, pr.Issue, comment) + } + } log.Trace("AddTestPullRequestTask [base_repo_id: %d, base_branch: %s]: finding pull requests", repoID, branch) prs, err = models.GetUnmergedPullRequestsByBaseInfo(repoID, branch) diff --git a/templates/repo/commits_list_small.tmpl b/templates/repo/commits_list_small.tmpl new file mode 100644 index 0000000000000..13f9a6d666bfb --- /dev/null +++ b/templates/repo/commits_list_small.tmpl @@ -0,0 +1,57 @@ +{{ $r:= List .Commits}} +{{ $index := 0}} +{{range $r}} + {{ $tag := printf "%s-%d" $.HashTag $index }} + {{ $index = Add $index 1}} +
@{{.Doer.Name}} mentioned you:
{{end}} + {{if eq .Comment.Type 29}} ++ {{.Doer.Name}} + {{if .Comment.IsForcePush}} + {{ $oldCommitLink:= printf "%s%s/%s/commit/%s" AppUrl .Comment.Issue.PullRequest.BaseRepo.OwnerName .Comment.Issue.PullRequest.BaseRepo.Name .Comment.OldCommit}} + {{ $newCommitLink:= printf "%s%s/%s/commit/%s" AppUrl .Comment.Issue.PullRequest.BaseRepo.OwnerName .Comment.Issue.PullRequest.BaseRepo.Name .Comment.NewCommit}} + force-pushed the {{.Comment.Issue.PullRequest.HeadBranch}} from + {{ShortSha .Comment.OldCommit}} + to + {{ShortSha .Comment.NewCommit}}. + {{else}} + {{if eq .Comment.Commits.Len 1}} + {{printf "pushed 1 commit to %s:" .Comment.Issue.PullRequest.HeadBranch}} + {{else}} + {{printf "pushed %d commits to %s:" .Comment.Commits.Len .Comment.Issue.PullRequest.HeadBranch}} + {{end}} + {{end}} +
+ {{end}}{{if eq .ActionName "close"}} Closed #{{.Issue.Index}}. @@ -46,7 +65,19 @@
{{.Patch}}