321 lines
7.7 KiB
Go
321 lines
7.7 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"crypto/tls"
|
|
"fmt"
|
|
"os"
|
|
|
|
log "github.com/Sirupsen/logrus"
|
|
"github.com/antonmedv/expr"
|
|
"github.com/aymerick/douceur/inliner"
|
|
"github.com/drone/drone-go/template"
|
|
"github.com/jaytaylor/html2text"
|
|
"github.com/urfave/cli"
|
|
gomail "gopkg.in/mail.v2"
|
|
)
|
|
|
|
type (
|
|
Repo struct {
|
|
FullName string
|
|
Owner string
|
|
Name string
|
|
SCM string
|
|
Link string
|
|
Avatar string
|
|
Branch string
|
|
Private bool
|
|
Trusted bool
|
|
}
|
|
|
|
Remote struct {
|
|
URL string
|
|
}
|
|
|
|
Author struct {
|
|
Name string
|
|
Email string
|
|
Avatar string
|
|
}
|
|
|
|
Commit struct {
|
|
Sha string
|
|
Ref string
|
|
Branch string
|
|
Link string
|
|
Message string
|
|
Author Author
|
|
}
|
|
|
|
Build struct {
|
|
Number int
|
|
Event string
|
|
Status string
|
|
Link string
|
|
Created int64
|
|
Started int64
|
|
Finished int64
|
|
}
|
|
|
|
PrevBuild struct {
|
|
Status string
|
|
Number int
|
|
}
|
|
|
|
PrevCommit struct {
|
|
Sha string
|
|
}
|
|
|
|
Prev struct {
|
|
Build PrevBuild
|
|
Commit PrevCommit
|
|
}
|
|
|
|
Job struct {
|
|
Status string
|
|
ExitCode int
|
|
Started int64
|
|
Finished int64
|
|
}
|
|
|
|
Yaml struct {
|
|
Signed bool
|
|
Verified bool
|
|
}
|
|
|
|
Config struct {
|
|
FromAddress string
|
|
FromName string
|
|
Host string
|
|
Port int
|
|
Username string
|
|
Password string
|
|
SkipVerify bool
|
|
NoStartTLS bool
|
|
Recipients []string
|
|
RecipientsFile string
|
|
RecipientsOnly bool
|
|
Subject string
|
|
Body string
|
|
Attachment string
|
|
Attachments []string
|
|
ClientHostname string
|
|
Evaluation string
|
|
}
|
|
|
|
Plugin struct {
|
|
Context *cli.Context
|
|
Repo Repo
|
|
Remote Remote
|
|
Commit Commit
|
|
Build Build
|
|
Prev Prev
|
|
Job Job
|
|
Yaml Yaml
|
|
Tag string
|
|
PullRequest int
|
|
DeployTo string
|
|
Config Config
|
|
}
|
|
)
|
|
|
|
// Exec will send emails over SMTP
|
|
func (p Plugin) Exec() error {
|
|
if p.Config.Evaluation != "" {
|
|
env := p.Environ()
|
|
|
|
fmt.Printf("%+v\n", expr.Env(env))
|
|
|
|
out, err := expr.Compile(p.Config.Evaluation, expr.Env(env), expr.AsBool())
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
result, err := expr.Run(out, env)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if result.(bool) == false {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
var dialer *gomail.Dialer
|
|
|
|
if !p.Config.RecipientsOnly {
|
|
exists := false
|
|
for _, recipient := range p.Config.Recipients {
|
|
if recipient == p.Commit.Author.Email {
|
|
exists = true
|
|
}
|
|
}
|
|
|
|
if !exists {
|
|
p.Config.Recipients = append(p.Config.Recipients, p.Commit.Author.Email)
|
|
}
|
|
}
|
|
|
|
if p.Config.RecipientsFile != "" {
|
|
f, err := os.Open(p.Config.RecipientsFile)
|
|
if err == nil {
|
|
scanner := bufio.NewScanner(f)
|
|
for scanner.Scan() {
|
|
p.Config.Recipients = append(p.Config.Recipients, scanner.Text())
|
|
}
|
|
} else {
|
|
log.Errorf("Could not open RecipientsFile %s: %v", p.Config.RecipientsFile, err)
|
|
}
|
|
}
|
|
|
|
if p.Config.Username == "" && p.Config.Password == "" {
|
|
dialer = &gomail.Dialer{Host: p.Config.Host, Port: p.Config.Port}
|
|
} else {
|
|
dialer = gomail.NewDialer(p.Config.Host, p.Config.Port, p.Config.Username, p.Config.Password)
|
|
}
|
|
|
|
if p.Config.SkipVerify {
|
|
dialer.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
|
}
|
|
|
|
if p.Config.NoStartTLS {
|
|
dialer.StartTLSPolicy = gomail.NoStartTLS
|
|
}
|
|
|
|
dialer.LocalName = p.Config.ClientHostname
|
|
|
|
closer, err := dialer.Dial()
|
|
if err != nil {
|
|
log.Errorf("Error while dialing SMTP server: %v", err)
|
|
return err
|
|
}
|
|
|
|
type Context struct {
|
|
Repo Repo
|
|
Remote Remote
|
|
Commit Commit
|
|
Build Build
|
|
Prev Prev
|
|
Job Job
|
|
Yaml Yaml
|
|
Tag string
|
|
PullRequest int
|
|
DeployTo string
|
|
}
|
|
ctx := Context{
|
|
Repo: p.Repo,
|
|
Remote: p.Remote,
|
|
Commit: p.Commit,
|
|
Build: p.Build,
|
|
Prev: p.Prev,
|
|
Job: p.Job,
|
|
Yaml: p.Yaml,
|
|
Tag: p.Tag,
|
|
PullRequest: p.PullRequest,
|
|
DeployTo: p.DeployTo,
|
|
}
|
|
|
|
// Render body in HTML and plain text
|
|
renderedBody, err := template.RenderTrim(p.Config.Body, ctx)
|
|
if err != nil {
|
|
log.Errorf("Could not render body template: %v", err)
|
|
return err
|
|
}
|
|
html, err := inliner.Inline(renderedBody)
|
|
if err != nil {
|
|
log.Errorf("Could not inline rendered body: %v", err)
|
|
return err
|
|
}
|
|
plainBody, err := html2text.FromString(html)
|
|
if err != nil {
|
|
log.Errorf("Could not convert html to text: %v", err)
|
|
return err
|
|
}
|
|
|
|
// Render subject
|
|
subject, err := template.RenderTrim(p.Config.Subject, ctx)
|
|
if err != nil {
|
|
log.Errorf("Could not render subject template: %v", err)
|
|
return err
|
|
}
|
|
|
|
// Send emails
|
|
message := gomail.NewMessage()
|
|
for _, recipient := range p.Config.Recipients {
|
|
if len(recipient) == 0 {
|
|
continue
|
|
}
|
|
message.SetAddressHeader("From", p.Config.FromAddress, p.Config.FromName)
|
|
message.SetAddressHeader("To", recipient, "")
|
|
message.SetHeader("Subject", subject)
|
|
message.AddAlternative("text/plain", plainBody)
|
|
message.AddAlternative("text/html", html)
|
|
|
|
if p.Config.Attachment != "" {
|
|
attach(message, p.Config.Attachment)
|
|
}
|
|
|
|
for _, attachment := range p.Config.Attachments {
|
|
attach(message, attachment)
|
|
}
|
|
|
|
if err := gomail.Send(closer, message); err != nil {
|
|
log.Errorf("Could not send email to %q: %v", recipient, err)
|
|
return err
|
|
}
|
|
message.Reset()
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p Plugin) Environ() map[string]string {
|
|
return map[string]string{
|
|
"CI_REPO_OWNER": p.Context.String("repo.owner"),
|
|
"CI_REPO_NAME": p.Context.String("repo.name"),
|
|
"CI_REPO_SCM": p.Context.String("repo.scm"),
|
|
"CI_REPO_LINK": p.Context.String("repo.link"),
|
|
"DRONE_REPO_AVATAR": p.Context.String("repo.avatar"),
|
|
"CI_REPO_DEFAULT_BRANCH": p.Context.String("repo.branch"),
|
|
"CI_REPO_PRIVATE": p.Context.String("repo.private"),
|
|
"DRONE_REPO_TRUSTED": p.Context.String("repo.trusted"),
|
|
"CI_REPO_CLONE_URL": p.Context.String("remote.url"),
|
|
"CI_COMMIT_SHA": p.Context.String("commit.sha"),
|
|
"CI_COMMIT_REF": p.Context.String("commit.ref"),
|
|
"CI_COMMIT_BRANCH": p.Context.String("commit.branch"),
|
|
"CI_COMMIT_LINK": p.Context.String("commit.link"),
|
|
"CI_COMMIT_MESSAGE": p.Context.String("commit.message"),
|
|
"CI_COMMIT_AUTHOR": p.Context.String("commit.author.name"),
|
|
"CI_COMMIT_AUTHOR_EMAIL": p.Context.String("commit.author.email"),
|
|
"CI_COMMIT_AUTHOR_AVATAR": p.Context.String("commit.author.avatar"),
|
|
"CI_BUILD_NUMBER": p.Context.String("build.number"),
|
|
"CI_BUILD_EVENT": p.Context.String("build.event"),
|
|
"CI_PIPELINE_STATUS": p.Context.String("build.status"),
|
|
"CI_PIPELINE_LINK": p.Context.String("build.link"),
|
|
"CI_PIPELINE_CREATED": p.Context.String("build.created"),
|
|
"CI_PIPELINE_STARTED": p.Context.String("build.started"),
|
|
"CI_PIPELINE_FINISHED": p.Context.String("build.finished"),
|
|
"CI_PREV_PIPELINE_STATUS": p.Context.String("prev.build.status"),
|
|
"CI_PREV_PIPELINE_NUMBER": p.Context.String("prev.build.number"),
|
|
"CI_PREV_COMMIT_SHA": p.Context.String("prev.commit.sha"),
|
|
"CI_STEP_NUMBER": p.Context.String("job.number"),
|
|
"CI_STEP_STATUS": p.Context.String("job.status"),
|
|
"DRONE_JOB_EXIT_CODE": p.Context.String("job.exitCode"),
|
|
"CI_STEP_STARTED": p.Context.String("job.started"),
|
|
"CI_STEP_FINISHED": p.Context.String("job.finished"),
|
|
"DRONE_YAML_SIGNED": p.Context.String("yaml.signed"),
|
|
"DRONE_YAML_VERIFIED": p.Context.String("yaml.verified"),
|
|
"CI_COMMIT_TAG": p.Context.String("tag"),
|
|
"CI_COMMIT_PULL_REQUEST": p.Context.String("pullRequest"),
|
|
"CI_PIPELINE_DEPLOY_TARGET": p.Context.String("deployTo"),
|
|
}
|
|
}
|
|
|
|
func attach(message *gomail.Message, attachment string) {
|
|
if _, err := os.Stat(attachment); err == nil {
|
|
message.Attach(attachment)
|
|
}
|
|
}
|