Implemented head_commit for webhooks (#16282)

* Removed Len field.

* Added head_commit webhook field.

* Added comment for returns.
This commit is contained in:
KN4CK3R 2021-06-29 15:34:03 +02:00 committed by GitHub
parent 579fcad8cd
commit aac663e0da
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 129 additions and 97 deletions

View file

@ -562,7 +562,7 @@ func (m *webhookNotifier) NotifyIssueChangeMilestone(doer *models.User, issue *m
func (m *webhookNotifier) NotifyPushCommits(pusher *models.User, repo *models.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) { func (m *webhookNotifier) NotifyPushCommits(pusher *models.User, repo *models.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
apiPusher := convert.ToUser(pusher, nil) apiPusher := convert.ToUser(pusher, nil)
apiCommits, err := commits.ToAPIPayloadCommits(repo.RepoPath(), repo.HTMLURL()) apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(repo.RepoPath(), repo.HTMLURL())
if err != nil { if err != nil {
log.Error("commits.ToAPIPayloadCommits failed: %v", err) log.Error("commits.ToAPIPayloadCommits failed: %v", err)
return return
@ -574,6 +574,7 @@ func (m *webhookNotifier) NotifyPushCommits(pusher *models.User, repo *models.Re
After: opts.NewCommitID, After: opts.NewCommitID,
CompareURL: setting.AppURL + commits.CompareURL, CompareURL: setting.AppURL + commits.CompareURL,
Commits: apiCommits, Commits: apiCommits,
HeadCommit: apiHeadCommit,
Repo: convert.ToRepo(repo, models.AccessModeOwner), Repo: convert.ToRepo(repo, models.AccessModeOwner),
Pusher: apiPusher, Pusher: apiPusher,
Sender: apiPusher, Sender: apiPusher,
@ -790,7 +791,7 @@ func (m *webhookNotifier) NotifyDeleteRelease(doer *models.User, rel *models.Rel
func (m *webhookNotifier) NotifySyncPushCommits(pusher *models.User, repo *models.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) { func (m *webhookNotifier) NotifySyncPushCommits(pusher *models.User, repo *models.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
apiPusher := convert.ToUser(pusher, nil) apiPusher := convert.ToUser(pusher, nil)
apiCommits, err := commits.ToAPIPayloadCommits(repo.RepoPath(), repo.HTMLURL()) apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(repo.RepoPath(), repo.HTMLURL())
if err != nil { if err != nil {
log.Error("commits.ToAPIPayloadCommits failed: %v", err) log.Error("commits.ToAPIPayloadCommits failed: %v", err)
return return
@ -802,6 +803,7 @@ func (m *webhookNotifier) NotifySyncPushCommits(pusher *models.User, repo *model
After: opts.NewCommitID, After: opts.NewCommitID,
CompareURL: setting.AppURL + commits.CompareURL, CompareURL: setting.AppURL + commits.CompareURL,
Commits: apiCommits, Commits: apiCommits,
HeadCommit: apiHeadCommit,
Repo: convert.ToRepo(repo, models.AccessModeOwner), Repo: convert.ToRepo(repo, models.AccessModeOwner),
Pusher: apiPusher, Pusher: apiPusher,
Sender: apiPusher, Sender: apiPusher,

View file

@ -28,8 +28,8 @@ type PushCommit struct {
// PushCommits represents list of commits in a push operation. // PushCommits represents list of commits in a push operation.
type PushCommits struct { type PushCommits struct {
Len int
Commits []*PushCommit Commits []*PushCommit
HeadCommit *PushCommit
CompareURL string CompareURL string
avatars map[string]string avatars map[string]string
@ -44,67 +44,88 @@ func NewPushCommits() *PushCommits {
} }
} }
// ToAPIPayloadCommits converts a PushCommits object to // toAPIPayloadCommit converts a single PushCommit to an api.PayloadCommit object.
// api.PayloadCommit format. func (pc *PushCommits) toAPIPayloadCommit(repoPath, repoLink string, commit *PushCommit) (*api.PayloadCommit, error) {
func (pc *PushCommits) ToAPIPayloadCommits(repoPath, repoLink string) ([]*api.PayloadCommit, error) { var err error
authorUsername := ""
author, ok := pc.emailUsers[commit.AuthorEmail]
if !ok {
author, err = models.GetUserByEmail(commit.AuthorEmail)
if err == nil {
authorUsername = author.Name
pc.emailUsers[commit.AuthorEmail] = author
}
} else {
authorUsername = author.Name
}
committerUsername := ""
committer, ok := pc.emailUsers[commit.CommitterEmail]
if !ok {
committer, err = models.GetUserByEmail(commit.CommitterEmail)
if err == nil {
// TODO: check errors other than email not found.
committerUsername = committer.Name
pc.emailUsers[commit.CommitterEmail] = committer
}
} else {
committerUsername = committer.Name
}
fileStatus, err := git.GetCommitFileStatus(repoPath, commit.Sha1)
if err != nil {
return nil, fmt.Errorf("FileStatus [commit_sha1: %s]: %v", commit.Sha1, err)
}
return &api.PayloadCommit{
ID: commit.Sha1,
Message: commit.Message,
URL: fmt.Sprintf("%s/commit/%s", repoLink, commit.Sha1),
Author: &api.PayloadUser{
Name: commit.AuthorName,
Email: commit.AuthorEmail,
UserName: authorUsername,
},
Committer: &api.PayloadUser{
Name: commit.CommitterName,
Email: commit.CommitterEmail,
UserName: committerUsername,
},
Added: fileStatus.Added,
Removed: fileStatus.Removed,
Modified: fileStatus.Modified,
Timestamp: commit.Timestamp,
}, nil
}
// ToAPIPayloadCommits converts a PushCommits object to api.PayloadCommit format.
// It returns all converted commits and, if provided, the head commit or an error otherwise.
func (pc *PushCommits) ToAPIPayloadCommits(repoPath, repoLink string) ([]*api.PayloadCommit, *api.PayloadCommit, error) {
commits := make([]*api.PayloadCommit, len(pc.Commits)) commits := make([]*api.PayloadCommit, len(pc.Commits))
var headCommit *api.PayloadCommit
if pc.emailUsers == nil { if pc.emailUsers == nil {
pc.emailUsers = make(map[string]*models.User) pc.emailUsers = make(map[string]*models.User)
} }
var err error
for i, commit := range pc.Commits { for i, commit := range pc.Commits {
authorUsername := "" apiCommit, err := pc.toAPIPayloadCommit(repoPath, repoLink, commit)
author, ok := pc.emailUsers[commit.AuthorEmail]
if !ok {
author, err = models.GetUserByEmail(commit.AuthorEmail)
if err == nil {
authorUsername = author.Name
pc.emailUsers[commit.AuthorEmail] = author
}
} else {
authorUsername = author.Name
}
committerUsername := ""
committer, ok := pc.emailUsers[commit.CommitterEmail]
if !ok {
committer, err = models.GetUserByEmail(commit.CommitterEmail)
if err == nil {
// TODO: check errors other than email not found.
committerUsername = committer.Name
pc.emailUsers[commit.CommitterEmail] = committer
}
} else {
committerUsername = committer.Name
}
fileStatus, err := git.GetCommitFileStatus(repoPath, commit.Sha1)
if err != nil { if err != nil {
return nil, fmt.Errorf("FileStatus [commit_sha1: %s]: %v", commit.Sha1, err) return nil, nil, err
} }
commits[i] = &api.PayloadCommit{ commits[i] = apiCommit
ID: commit.Sha1, if pc.HeadCommit != nil && pc.HeadCommit.Sha1 == commits[i].ID {
Message: commit.Message, headCommit = apiCommit
URL: fmt.Sprintf("%s/commit/%s", repoLink, commit.Sha1),
Author: &api.PayloadUser{
Name: commit.AuthorName,
Email: commit.AuthorEmail,
UserName: authorUsername,
},
Committer: &api.PayloadUser{
Name: commit.CommitterName,
Email: commit.CommitterEmail,
UserName: committerUsername,
},
Added: fileStatus.Added,
Removed: fileStatus.Removed,
Modified: fileStatus.Modified,
Timestamp: commit.Timestamp,
} }
} }
return commits, nil if pc.HeadCommit != nil && headCommit == nil {
var err error
headCommit, err = pc.toAPIPayloadCommit(repoPath, repoLink, pc.HeadCommit)
if err != nil {
return nil, nil, err
}
}
return commits, headCommit, nil
} }
// AvatarLink tries to match user in database with e-mail // AvatarLink tries to match user in database with e-mail
@ -157,13 +178,9 @@ func CommitToPushCommit(commit *git.Commit) *PushCommit {
// ListToPushCommits transforms a list.List to PushCommits type. // ListToPushCommits transforms a list.List to PushCommits type.
func ListToPushCommits(l *list.List) *PushCommits { func ListToPushCommits(l *list.List) *PushCommits {
var commits []*PushCommit var commits []*PushCommit
var actEmail string
for e := l.Front(); e != nil; e = e.Next() { for e := l.Front(); e != nil; e = e.Next() {
commit := e.Value.(*git.Commit) commit := CommitToPushCommit(e.Value.(*git.Commit))
if actEmail == "" { commits = append(commits, commit)
actEmail = commit.Committer.Email
}
commits = append(commits, CommitToPushCommit(commit))
} }
return &PushCommits{l.Len(), commits, "", make(map[string]string), make(map[string]*models.User)} return &PushCommits{commits, nil, "", make(map[string]string), make(map[string]*models.User)}
} }

View file

@ -46,12 +46,13 @@ func TestPushCommits_ToAPIPayloadCommits(t *testing.T) {
Message: "good signed commit", Message: "good signed commit",
}, },
} }
pushCommits.Len = len(pushCommits.Commits) pushCommits.HeadCommit = &PushCommit{Sha1: "69554a6"}
repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository) repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository)
payloadCommits, err := pushCommits.ToAPIPayloadCommits(repo.RepoPath(), "/user2/repo16") payloadCommits, headCommit, err := pushCommits.ToAPIPayloadCommits(repo.RepoPath(), "/user2/repo16")
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, payloadCommits, 3) assert.Len(t, payloadCommits, 3)
assert.NotNil(t, headCommit)
assert.Equal(t, "69554a6", payloadCommits[0].ID) assert.Equal(t, "69554a6", payloadCommits[0].ID)
assert.Equal(t, "not signed commit", payloadCommits[0].Message) assert.Equal(t, "not signed commit", payloadCommits[0].Message)
@ -85,6 +86,17 @@ func TestPushCommits_ToAPIPayloadCommits(t *testing.T) {
assert.EqualValues(t, []string{"readme.md"}, payloadCommits[2].Added) assert.EqualValues(t, []string{"readme.md"}, payloadCommits[2].Added)
assert.EqualValues(t, []string{}, payloadCommits[2].Removed) assert.EqualValues(t, []string{}, payloadCommits[2].Removed)
assert.EqualValues(t, []string{}, payloadCommits[2].Modified) assert.EqualValues(t, []string{}, payloadCommits[2].Modified)
assert.Equal(t, "69554a6", headCommit.ID)
assert.Equal(t, "not signed commit", headCommit.Message)
assert.Equal(t, "/user2/repo16/commit/69554a6", headCommit.URL)
assert.Equal(t, "User2", headCommit.Committer.Name)
assert.Equal(t, "user2", headCommit.Committer.UserName)
assert.Equal(t, "User2", headCommit.Author.Name)
assert.Equal(t, "user2", headCommit.Author.UserName)
assert.EqualValues(t, []string{}, headCommit.Added)
assert.EqualValues(t, []string{}, headCommit.Removed)
assert.EqualValues(t, []string{"readme.md"}, headCommit.Modified)
} }
func TestPushCommits_AvatarLink(t *testing.T) { func TestPushCommits_AvatarLink(t *testing.T) {
@ -109,7 +121,6 @@ func TestPushCommits_AvatarLink(t *testing.T) {
Message: "message2", Message: "message2",
}, },
} }
pushCommits.Len = len(pushCommits.Commits)
assert.Equal(t, assert.Equal(t,
"https://secure.gravatar.com/avatar/ab53a2911ddf9b4817ac01ddcd3d975f?d=identicon&s=112", "https://secure.gravatar.com/avatar/ab53a2911ddf9b4817ac01ddcd3d975f?d=identicon&s=112",
@ -177,7 +188,6 @@ func TestListToPushCommits(t *testing.T) {
}) })
pushCommits := ListToPushCommits(l) pushCommits := ListToPushCommits(l)
assert.Equal(t, 2, pushCommits.Len)
if assert.Len(t, pushCommits.Commits, 2) { if assert.Len(t, pushCommits.Commits, 2) {
assert.Equal(t, "Message1", pushCommits.Commits[0].Message) assert.Equal(t, "Message1", pushCommits.Commits[0].Message)
assert.Equal(t, hexString1, pushCommits.Commits[0].Sha1) assert.Equal(t, hexString1, pushCommits.Commits[0].Sha1)

View file

@ -140,16 +140,17 @@ func TestHook(ctx *context.APIContext) {
return return
} }
commit := convert.ToPayloadCommit(ctx.Repo.Repository, ctx.Repo.Commit)
if err := webhook.PrepareWebhook(hook, ctx.Repo.Repository, models.HookEventPush, &api.PushPayload{ if err := webhook.PrepareWebhook(hook, ctx.Repo.Repository, models.HookEventPush, &api.PushPayload{
Ref: git.BranchPrefix + ctx.Repo.Repository.DefaultBranch, Ref: git.BranchPrefix + ctx.Repo.Repository.DefaultBranch,
Before: ctx.Repo.Commit.ID.String(), Before: ctx.Repo.Commit.ID.String(),
After: ctx.Repo.Commit.ID.String(), After: ctx.Repo.Commit.ID.String(),
Commits: []*api.PayloadCommit{ Commits: []*api.PayloadCommit{commit},
convert.ToPayloadCommit(ctx.Repo.Repository, ctx.Repo.Commit), HeadCommit: commit,
}, Repo: convert.ToRepo(ctx.Repo.Repository, models.AccessModeNone),
Repo: convert.ToRepo(ctx.Repo.Repository, models.AccessModeNone), Pusher: convert.ToUserWithAccessMode(ctx.User, models.AccessModeNone),
Pusher: convert.ToUserWithAccessMode(ctx.User, models.AccessModeNone), Sender: convert.ToUserWithAccessMode(ctx.User, models.AccessModeNone),
Sender: convert.ToUserWithAccessMode(ctx.User, models.AccessModeNone),
}); err != nil { }); err != nil {
ctx.Error(http.StatusInternalServerError, "PrepareWebhook: ", err) ctx.Error(http.StatusInternalServerError, "PrepareWebhook: ", err)
return return

View file

@ -1085,28 +1085,30 @@ func TestWebhook(ctx *context.Context) {
} }
apiUser := convert.ToUserWithAccessMode(ctx.User, models.AccessModeNone) apiUser := convert.ToUserWithAccessMode(ctx.User, models.AccessModeNone)
p := &api.PushPayload{
Ref: git.BranchPrefix + ctx.Repo.Repository.DefaultBranch, apiCommit := &api.PayloadCommit{
Before: commit.ID.String(), ID: commit.ID.String(),
After: commit.ID.String(), Message: commit.Message(),
Commits: []*api.PayloadCommit{ URL: ctx.Repo.Repository.HTMLURL() + "/commit/" + commit.ID.String(),
{ Author: &api.PayloadUser{
ID: commit.ID.String(), Name: commit.Author.Name,
Message: commit.Message(), Email: commit.Author.Email,
URL: ctx.Repo.Repository.HTMLURL() + "/commit/" + commit.ID.String(),
Author: &api.PayloadUser{
Name: commit.Author.Name,
Email: commit.Author.Email,
},
Committer: &api.PayloadUser{
Name: commit.Committer.Name,
Email: commit.Committer.Email,
},
},
}, },
Repo: convert.ToRepo(ctx.Repo.Repository, models.AccessModeNone), Committer: &api.PayloadUser{
Pusher: apiUser, Name: commit.Committer.Name,
Sender: apiUser, Email: commit.Committer.Email,
},
}
p := &api.PushPayload{
Ref: git.BranchPrefix + ctx.Repo.Repository.DefaultBranch,
Before: commit.ID.String(),
After: commit.ID.String(),
Commits: []*api.PayloadCommit{apiCommit},
HeadCommit: apiCommit,
Repo: convert.ToRepo(ctx.Repo.Repository, models.AccessModeNone),
Pusher: apiUser,
Sender: apiUser,
} }
if err := webhook.PrepareWebhook(w, ctx.Repo.Repository, models.HookEventPush, p); err != nil { if err := webhook.PrepareWebhook(w, ctx.Repo.Repository, models.HookEventPush, p); err != nil {
ctx.Flash.Error("PrepareWebhook: " + err.Error()) ctx.Flash.Error("PrepareWebhook: " + err.Error())

View file

@ -95,7 +95,6 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
if opts.IsNewRef() && opts.IsDelRef() { if opts.IsNewRef() && opts.IsDelRef() {
return fmt.Errorf("Old and new revisions are both %s", git.EmptySHA) return fmt.Errorf("Old and new revisions are both %s", git.EmptySHA)
} }
var commits = &repo_module.PushCommits{}
if opts.IsTag() { // If is tag reference if opts.IsTag() { // If is tag reference
if pusher == nil || pusher.ID != opts.PusherID { if pusher == nil || pusher.ID != opts.PusherID {
var err error var err error
@ -192,7 +191,8 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
} }
} }
commits = repo_module.ListToPushCommits(l) commits := repo_module.ListToPushCommits(l)
commits.HeadCommit = repo_module.CommitToPushCommit(newCommit)
if err := repofiles.UpdateIssuesCommit(pusher, repo, commits.Commits, refName); err != nil { if err := repofiles.UpdateIssuesCommit(pusher, repo, commits.Commits, refName); err != nil {
log.Error("updateIssuesCommit: %v", err) log.Error("updateIssuesCommit: %v", err)

View file

@ -101,7 +101,7 @@
</li> </li>
{{end}} {{end}}
{{end}} {{end}}
{{if and (gt $push.Len 1) $push.CompareURL}}<li><a href="{{AppSubUrl}}/{{$push.CompareURL}}">{{$.i18n.Tr "action.compare_commits" $push.Len}} »</a></li>{{end}} {{if and (gt (len $push.Commits) 1) $push.CompareURL}}<li><a href="{{AppSubUrl}}/{{$push.CompareURL}}">{{$.i18n.Tr "action.compare_commits" (len $push.Commits)}} »</a></li>{{end}}
</ul> </ul>
</div> </div>
{{else if eq .GetOpType 6}} {{else if eq .GetOpType 6}}