woodpecker/plugin/notify/email/email.go
Michael Nutt e8b993e7da Add support for after_success and after_failure to email notifications
This allows you to restrict email notifications to only be sent after the build changes from success to failure or failure to success.  It errs on the side of sending the notification; if the build is in another state (hung, for instance) or there was no previous build on the branch the email will also be sent.

Since the notify plugin shouldn't really have any responsibility for querying the database to find the previous commit's status, we store it on the commit when we save it.
2014-12-30 11:37:57 -05:00

186 lines
4.9 KiB
Go

package email
import (
"bytes"
"fmt"
"net"
"net/smtp"
"strings"
"github.com/drone/config"
"github.com/drone/drone/shared/model"
)
const (
NotifyAlways = "always" // always send email notification
NotifyNever = "never" // never send email notifications
NotifyAuthor = "author" // only send email notifications to the author
NotifyAfterFailure = "after_failure" // only send a notification if the previous commit failed
NotifyAfterSuccess = "after_success" // only send a notification if the previous commit succeeded
NotifyTrue = "true" // alias for NotifyTrue
NotifyFalse = "false" // alias for NotifyFalse
NotifyOn = "on" // alias for NotifyTrue
NotifyOff = "off" // alias for NotifyFalse
NotifyBlame = "blame" // alias for NotifyAuthor
)
const (
Subject = "[%s] %s/%s (%s - %s)"
)
var (
DefaultHost = config.String("smtp-host", "")
DefaultPort = config.String("smtp-port", "")
DefaultFrom = config.String("smtp-from", "")
DefaultUser = config.String("smtp-user", "")
DefaultPass = config.String("smtp-pass", "")
)
type Email struct {
Recipients []string `yaml:"recipients"`
Success string `yaml:"on_success"`
Failure string `yaml:"on_failure"`
Host string `yaml:"host"`
Port string `yaml:"port"`
From string `yaml:"from"`
Username string `yaml:"username"`
Password string `yaml:"password"`
}
// Send will send an email, either success or failure,
// based on the Commit Status.
func (e *Email) Send(context *model.Request) error {
var status = context.Commit.Status
switch status {
// no builds are triggered for pending builds
case model.StatusEnqueue, model.StatusStarted:
return nil
case model.StatusSuccess:
return e.sendSuccess(context)
default:
return e.sendFailure(context)
}
}
// sendFailure sends email notifications to the list of
// recipients indicating the build failed.
func (e *Email) sendFailure(context *model.Request) error {
switch e.Failure {
case NotifyFalse, NotifyNever, NotifyOff:
return nil
// if the last commit in this branch was a success, notify
case NotifyAfterSuccess:
if context.Commit.PriorStatus != "Success" {
return nil
}
// if the last commit in this branch was a failure, notify
case NotifyAfterFailure:
if context.Commit.PriorStatus != "Failure" {
return nil
}
// if configured to email the author, replace
// the recipiends with the commit author email.
case NotifyBlame, NotifyAuthor:
e.Recipients = []string{context.Commit.Author}
}
// generate the email failure template
var buf bytes.Buffer
err := failureTemplate.ExecuteTemplate(&buf, "_", context)
if err != nil {
return err
}
// generate the email subject
var subject = fmt.Sprintf(
Subject,
context.Commit.Status,
context.Repo.Owner,
context.Repo.Name,
context.Commit.Branch,
context.Commit.ShaShort(),
)
return e.send(subject, buf.String(), e.Recipients)
}
// sendSuccess sends email notifications to the list of
// recipients indicating the build was a success.
func (e *Email) sendSuccess(context *model.Request) error {
switch e.Success {
case NotifyFalse, NotifyNever, NotifyOff:
return nil
// if the last commit in this branch was a success, notify
case NotifyAfterSuccess:
if context.Commit.PriorStatus == "Failure" {
return nil
}
// if the last commit in this branch was a failure, notify
case NotifyAfterFailure:
if context.Commit.PriorStatus == "Success" {
return nil
}
// if configured to email the author, replace
// the recipiends with the commit author email.
case NotifyBlame, NotifyAuthor:
e.Recipients = []string{context.Commit.Author}
}
// generate the email success template
var buf bytes.Buffer
err := successTemplate.ExecuteTemplate(&buf, "_", context)
if err != nil {
return err
}
// generate the email subject
var subject = fmt.Sprintf(
Subject,
context.Commit.Status,
context.Repo.Owner,
context.Repo.Name,
context.Commit.Branch,
context.Commit.ShaShort(),
)
return e.send(subject, buf.String(), e.Recipients)
}
func (e *Email) send(subject, body string, recipients []string) error {
if len(recipients) == 0 {
return nil
}
// the user can provide their own smtp server
// configuration. If None provided, attempt to
// use the global configuration set in the environet
// variables.
if len(*DefaultHost) != 0 {
e.Host = *DefaultHost
e.Port = *DefaultPort
e.From = *DefaultFrom
e.Username = *DefaultUser
e.Password = *DefaultPass
}
var auth smtp.Auth
var addr = net.JoinHostPort(e.Host, e.Port)
// setup the authentication to the smtp server
// if the username and password are provided.
if len(e.Username) > 0 {
auth = smtp.PlainAuth("", e.Username, e.Password, e.Host)
}
// genereate the raw email message
var to = strings.Join(e.Recipients, ",")
var raw = fmt.Sprintf(rawMessage, e.From, to, subject, body)
return smtp.SendMail(addr, auth, e.From, e.Recipients, []byte(raw))
}