woodpecker-email/plugin.go
2023-01-04 13:12:43 +01:00

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)
}
}