Browse Source

hook: fix email not sent after push (#4430)

Turns out mail service was not initialized at all, also mail must
be sent in sync in hook mode before program exits.
Unknwon 7 years ago
parent
commit
6bc11c4450
10 changed files with 72 additions and 28 deletions
  1. 10 0
      cmd/hook.go
  2. 1 1
      gogs.go
  3. 1 2
      models/action.go
  4. 3 3
      models/comment.go
  5. 1 2
      models/issue_mail.go
  6. 6 6
      pkg/mailer/mail.go
  7. 17 4
      pkg/mailer/mailer.go
  8. 23 1
      pkg/setting/setting.go
  9. 9 8
      pkg/template/template.go
  10. 1 1
      templates/.VERSION

+ 10 - 0
cmd/hook.go

@@ -11,6 +11,7 @@ import (
 	"fmt"
 	"os"
 	"os/exec"
+	"path"
 	"path/filepath"
 	"strings"
 
@@ -22,7 +23,9 @@ import (
 
 	"github.com/gogits/gogs/models"
 	"github.com/gogits/gogs/pkg/httplib"
+	"github.com/gogits/gogs/pkg/mailer"
 	"github.com/gogits/gogs/pkg/setting"
+	"github.com/gogits/gogs/pkg/template"
 	http "github.com/gogits/gogs/routes/repo"
 )
 
@@ -184,6 +187,13 @@ func runHookPostReceive(c *cli.Context) error {
 	}
 	setup(c, "hooks/post-receive.log", true)
 
+	// Post-receive hook does more than just gather Git information,
+	// so we need to setup additional services for email notifications.
+	setting.NewPostReceiveHookServices()
+	mailer.NewContext()
+	mailer.InitMailRender(path.Join(setting.StaticRootPath, "templates/mail"),
+		path.Join(setting.CustomPath, "templates/mail"), template.NewFuncMap())
+
 	isWiki := strings.Contains(os.Getenv(http.ENV_REPO_CUSTOM_HOOKS_PATH), ".wiki.git/")
 
 	buf := bytes.NewBuffer(nil)

+ 1 - 1
gogs.go

@@ -16,7 +16,7 @@ import (
 	"github.com/gogits/gogs/pkg/setting"
 )
 
-const APP_VER = "0.11.28.0718"
+const APP_VER = "0.11.29.0727"
 
 func init() {
 	setting.AppVer = APP_VER

+ 1 - 2
models/action.go

@@ -379,8 +379,7 @@ func UpdateIssuesCommit(doer *User, repo *Repository, commits []*PushCommit) err
 			if ref[0] == '#' {
 				ref = fmt.Sprintf("%s%s", repo.FullName(), ref)
 			} else if !strings.Contains(ref, "/") {
-				// We don't support User#ID syntax yet
-				// return ErrNotImplemented
+				// FIXME: We don't support User#ID syntax yet
 				continue
 			}
 

+ 3 - 3
models/comment.go

@@ -188,7 +188,7 @@ func (cmt *Comment) mailParticipants(e Engine, opType ActionType, issue *Issue)
 		issue.Content = fmt.Sprintf("Reopened #%d", issue.Index)
 	}
 	if err = mailIssueCommentToParticipants(issue, cmt.Poster, mentions); err != nil {
-		log.Error(4, "mailIssueCommentToParticipants: %v", err)
+		log.Error(2, "mailIssueCommentToParticipants: %v", err)
 	}
 
 	return nil
@@ -290,10 +290,10 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
 	// Notify watchers for whatever action comes in, ignore if no action type.
 	if act.OpType > 0 {
 		if err = notifyWatchers(e, act); err != nil {
-			log.Error(4, "notifyWatchers: %v", err)
+			log.Error(2, "notifyWatchers: %v", err)
 		}
 		if err = comment.mailParticipants(e, act.OpType, opts.Issue); err != nil {
-			log.Error(4, "MailParticipants: %v", err)
+			log.Error(2, "MailParticipants: %v", err)
 		}
 	}
 

+ 1 - 2
models/issue_mail.go

@@ -155,7 +155,6 @@ func mailIssueCommentToParticipants(issue *Issue, doer *User, mentions []string)
 		tos = append(tos, mentions[i])
 	}
 	mailer.SendIssueMentionMail(NewMailerIssue(issue), NewMailerRepo(issue.Repo), NewMailerUser(doer), GetUserEmailsByNames(tos))
-
 	return nil
 }
 
@@ -168,7 +167,7 @@ func (issue *Issue) MailParticipants() (err error) {
 	}
 
 	if err = mailIssueCommentToParticipants(issue, issue.Poster, mentions); err != nil {
-		log.Error(4, "mailIssueCommentToParticipants: %v", err)
+		log.Error(2, "mailIssueCommentToParticipants: %v", err)
 	}
 
 	return nil

+ 6 - 6
pkg/mailer/mail.go

@@ -94,7 +94,7 @@ func SendUserMail(c *macaron.Context, u User, tpl, code, subject, info string) {
 	msg := NewMessage([]string{u.Email()}, subject, body)
 	msg.Info = fmt.Sprintf("UID: %d, %s", u.ID(), info)
 
-	SendAsync(msg)
+	Send(msg)
 }
 
 func SendActivateAccountMail(c *macaron.Context, u User) {
@@ -122,7 +122,7 @@ func SendActivateEmailMail(c *macaron.Context, u User, email string) {
 	msg := NewMessage([]string{email}, c.Tr("mail.activate_email"), body)
 	msg.Info = fmt.Sprintf("UID: %d, activate email", u.ID())
 
-	SendAsync(msg)
+	Send(msg)
 }
 
 // SendRegisterNotifyMail triggers a notify e-mail by admin created a account.
@@ -139,7 +139,7 @@ func SendRegisterNotifyMail(c *macaron.Context, u User) {
 	msg := NewMessage([]string{u.Email()}, c.Tr("mail.register_notify"), body)
 	msg.Info = fmt.Sprintf("UID: %d, registration notify", u.ID())
 
-	SendAsync(msg)
+	Send(msg)
 }
 
 // SendCollaboratorMail sends mail notification to new collaborator.
@@ -160,7 +160,7 @@ func SendCollaboratorMail(u, doer User, repo Repository) {
 	msg := NewMessage([]string{u.Email()}, subject, body)
 	msg.Info = fmt.Sprintf("UID: %d, add collaborator", u.ID())
 
-	SendAsync(msg)
+	Send(msg)
 }
 
 func composeTplData(subject, body, link string) map[string]interface{} {
@@ -192,7 +192,7 @@ func SendIssueCommentMail(issue Issue, repo Repository, doer User, tos []string)
 		return
 	}
 
-	SendAsync(composeIssueMessage(issue, repo, doer, MAIL_ISSUE_COMMENT, tos, "issue comment"))
+	Send(composeIssueMessage(issue, repo, doer, MAIL_ISSUE_COMMENT, tos, "issue comment"))
 }
 
 // SendIssueMentionMail composes and sends issue mention emails to target receivers.
@@ -200,5 +200,5 @@ func SendIssueMentionMail(issue Issue, repo Repository, doer User, tos []string)
 	if len(tos) == 0 {
 		return
 	}
-	SendAsync(composeIssueMessage(issue, repo, doer, MAIL_ISSUE_MENTION, tos, "issue mention"))
+	Send(composeIssueMessage(issue, repo, doer, MAIL_ISSUE_MENTION, tos, "issue mention"))
 }

+ 17 - 4
pkg/mailer/mailer.go

@@ -24,6 +24,7 @@ import (
 type Message struct {
 	Info string // Message information for log purpose.
 	*gomail.Message
+	confirmChan chan struct{}
 }
 
 // NewMessageFrom creates new mail message object with custom From header.
@@ -48,9 +49,9 @@ func NewMessageFrom(to []string, from, subject, htmlBody string) *Message {
 		}
 	}
 	msg.SetBody(contentType, body)
-
 	return &Message{
-		Message: msg,
+		Message:     msg,
+		confirmChan: make(chan struct{}),
 	}
 }
 
@@ -204,12 +205,14 @@ func processMailQueue() {
 			} else {
 				log.Trace("E-mails sent %s: %s", msg.GetHeader("To"), msg.Info)
 			}
+			msg.confirmChan <- struct{}{}
 		}
 	}
 }
 
 var mailQueue chan *Message
 
+// NewContext initializes settings for mailer.
 func NewContext() {
 	// Need to check if mailQueue is nil because in during reinstall (user had installed
 	// before but swithed install lock off), this function will be called again
@@ -222,8 +225,18 @@ func NewContext() {
 	go processMailQueue()
 }
 
-func SendAsync(msg *Message) {
+// Send puts new message object into mail queue.
+// It returns without confirmation (mail processed asynchronously) in normal cases,
+// but waits/blocks under hook mode to make sure mail has been sent.
+func Send(msg *Message) {
+	mailQueue <- msg
+
+	if setting.HookMode {
+		<-msg.confirmChan
+		return
+	}
+
 	go func() {
-		mailQueue <- msg
+		<-msg.confirmChan
 	}()
 }

+ 23 - 1
pkg/setting/setting.go

@@ -832,9 +832,10 @@ var (
 	MailService *Mailer
 )
 
+// newMailService initializes mail service options from configuration.
+// No non-error log will be printed in hook mode.
 func newMailService() {
 	sec := Cfg.Section("mailer")
-	// Check mailer setting.
 	if !sec.Key("ENABLED").MustBool() {
 		return
 	}
@@ -863,6 +864,9 @@ func newMailService() {
 		MailService.FromEmail = parsed.Address
 	}
 
+	if HookMode {
+		return
+	}
 	log.Info("Mail Service Enabled")
 }
 
@@ -877,6 +881,8 @@ func newRegisterMailService() {
 	log.Info("Register Mail Service Enabled")
 }
 
+// newNotifyMailService initializes notification email service options from configuration.
+// No non-error log will be printed in hook mode.
 func newNotifyMailService() {
 	if !Cfg.Section("service").Key("ENABLE_NOTIFY_MAIL").MustBool() {
 		return
@@ -885,6 +891,10 @@ func newNotifyMailService() {
 		return
 	}
 	Service.EnableNotifyMail = true
+
+	if HookMode {
+		return
+	}
 	log.Info("Notify Mail Service Enabled")
 }
 
@@ -901,3 +911,15 @@ func NewServices() {
 	newRegisterMailService()
 	newNotifyMailService()
 }
+
+// HookMode indicates whether program starts as Git server-side hook callback.
+var HookMode bool
+
+// NewPostReceiveHookServices initializes all services that are needed by
+// Git server-side post-receive hook callback.
+func NewPostReceiveHookServices() {
+	HookMode = true
+	newService()
+	newMailService()
+	newNotifyMailService()
+}

+ 9 - 8
pkg/template/template.go

@@ -22,11 +22,12 @@ import (
 	"gopkg.in/editorconfig/editorconfig-core-go.v1"
 
 	"github.com/gogits/gogs/models"
-	"github.com/gogits/gogs/pkg/tool"
 	"github.com/gogits/gogs/pkg/markup"
 	"github.com/gogits/gogs/pkg/setting"
+	"github.com/gogits/gogs/pkg/tool"
 )
 
+// TODO: only initialize map once and save to a local variable to reduce copies.
 func NewFuncMap() []template.FuncMap {
 	return []template.FuncMap{map[string]interface{}{
 		"GoVer": func() string {
@@ -91,13 +92,13 @@ func NewFuncMap() []template.FuncMap {
 			}
 			return str[start:end]
 		},
-		"Join":                  strings.Join,
-		"EllipsisString":        tool.EllipsisString,
-		"DiffTypeToStr":         DiffTypeToStr,
-		"DiffLineTypeToStr":     DiffLineTypeToStr,
-		"Sha1":                  Sha1,
-		"ShortSHA1":              tool.ShortSHA1,
-		"MD5":                   tool.MD5,
+		"Join":              strings.Join,
+		"EllipsisString":    tool.EllipsisString,
+		"DiffTypeToStr":     DiffTypeToStr,
+		"DiffLineTypeToStr": DiffLineTypeToStr,
+		"Sha1":              Sha1,
+		"ShortSHA1":         tool.ShortSHA1,
+		"MD5":               tool.MD5,
 		"ActionContent2Commits": ActionContent2Commits,
 		"EscapePound":           EscapePound,
 		"RenderCommitMessage":   RenderCommitMessage,

+ 1 - 1
templates/.VERSION

@@ -1 +1 @@
-0.11.28.0718
+0.11.29.0727