Integrated skip verify, gomail and templates
I have switched the mail sending mechanism to gomail to get more power out of the sending method. Beside that I have added a flag to skip certificate verification. On top of that I have integrated the drone templating to make it possible to overwrite the email templates optionally. To be more compatible I have created a plaintext message format with the help of html2text as well.
This commit is contained in:
parent
c0654081b5
commit
8484730b82
56
DOCS.md
56
DOCS.md
|
@ -22,3 +22,59 @@ notify:
|
|||
recipients:
|
||||
- octocat@github.com
|
||||
```
|
||||
|
||||
### Custom Templates
|
||||
|
||||
In some cases you may want to customize the look and feel of the email message
|
||||
so you can use custom templates. For the use case we expose the following
|
||||
additional parameters, all of the accept a custom handlebars template, directly
|
||||
provided as a string or as a remote URL which gets fetched and parsed:
|
||||
|
||||
* `subject` - A handlebars template to create a custom subject. For more
|
||||
details take a look at the [docs](http://handlebarsjs.com/). You can see the
|
||||
default template [here](https://github.com/drone-plugins/drone-email/blob/master/template.go#L4)
|
||||
* `template` - A handlebars template to create a custom template. For more
|
||||
details take a look at the [docs](http://handlebarsjs.com/). You can see the
|
||||
default template [here](https://github.com/drone-plugins/drone-email/blob/master/template.go#L8-L292)
|
||||
|
||||
Example configuration that generate a custom email:
|
||||
|
||||
```yaml
|
||||
notify:
|
||||
email:
|
||||
from: noreply@github.com
|
||||
host: smtp.mailgun.org
|
||||
username: octocat
|
||||
password: 12345
|
||||
recipients:
|
||||
- octocat@github.com
|
||||
subject: >
|
||||
[{{ build.status }}]
|
||||
{{ repo.owner }}/{{ repo.name }}
|
||||
({{ build.branch }} - {{ truncate build.commit 8 }})
|
||||
template: >
|
||||
https://git.io/vgvPz
|
||||
```
|
||||
|
||||
### Skip SSL verify
|
||||
|
||||
In some cases you may want to skip SSL verification, even if we discourage that
|
||||
as it leads to an unsecure environment. Please use this option only within your
|
||||
intranet and/or with truested resources. For this use case we expose the
|
||||
following additional parameter:
|
||||
|
||||
* `skip_verify` - Skip verification of SSL certificates
|
||||
|
||||
Example configuration that skips SSL verification:
|
||||
|
||||
```yaml
|
||||
notify:
|
||||
email:
|
||||
from: noreply@github.com
|
||||
host: smtp.mailgun.org
|
||||
username: octocat
|
||||
password: 12345
|
||||
skip_verify: true
|
||||
recipients:
|
||||
- octocat@github.com
|
||||
```
|
||||
|
|
|
@ -35,7 +35,7 @@ make deps build
|
|||
"finished_at": 1421029813,
|
||||
"message": "Update the Readme",
|
||||
"author": "johnsmith",
|
||||
"author_email": "john.smith@gmail.com"
|
||||
"author_email": "john.smith@gmail.com",
|
||||
"event": "push",
|
||||
"branch": "master",
|
||||
"commit": "436b7a6e2abaddfd35740527353e78a227ddcb2c",
|
||||
|
|
10
main.go
10
main.go
|
@ -32,6 +32,14 @@ func main() {
|
|||
}
|
||||
}
|
||||
|
||||
if vargs.Subject == "" {
|
||||
vargs.Subject = defaultSubject
|
||||
}
|
||||
|
||||
if vargs.Template == "" {
|
||||
vargs.Template = defaultTemplate
|
||||
}
|
||||
|
||||
if vargs.Port == 0 {
|
||||
vargs.Port = 587
|
||||
}
|
||||
|
@ -45,8 +53,6 @@ func main() {
|
|||
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
153
sender.go
153
sender.go
|
@ -1,116 +1,113 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/smtp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"crypto/tls"
|
||||
|
||||
"github.com/drone/drone-go/drone"
|
||||
)
|
||||
|
||||
const (
|
||||
Subject = "[%s] %s/%s (%s - %s)"
|
||||
"github.com/drone/drone-go/template"
|
||||
"github.com/go-gomail/gomail"
|
||||
"github.com/jaytaylor/html2text"
|
||||
)
|
||||
|
||||
func Send(context *Context) error {
|
||||
switch context.Build.Status {
|
||||
case drone.StatusSuccess:
|
||||
return SendSuccess(context)
|
||||
default:
|
||||
return SendFailure(context)
|
||||
payload := &drone.Payload{
|
||||
System: &context.System,
|
||||
Repo: &context.Repo,
|
||||
Build: &context.Build,
|
||||
}
|
||||
}
|
||||
|
||||
// SendFailure sends email notifications to the list of
|
||||
// recipients indicating the build failed.
|
||||
func SendFailure(context *Context) error {
|
||||
// generate the email failure template
|
||||
var buf bytes.Buffer
|
||||
err := failureTemplate.ExecuteTemplate(&buf, "_", context)
|
||||
subject, plain, html, err := build(
|
||||
payload,
|
||||
context,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// generate the email subject
|
||||
var subject = fmt.Sprintf(
|
||||
Subject,
|
||||
context.Build.Status,
|
||||
context.Repo.Owner,
|
||||
context.Repo.Name,
|
||||
context.Build.Branch,
|
||||
context.Build.Commit[:8],
|
||||
return send(
|
||||
subject,
|
||||
plain,
|
||||
html,
|
||||
context,
|
||||
)
|
||||
|
||||
return send(subject, buf.String(), context)
|
||||
}
|
||||
|
||||
// SendSuccess sends email notifications to the list of
|
||||
// recipients indicating the build was a success.
|
||||
func SendSuccess(context *Context) error {
|
||||
// generate the email success template
|
||||
var buf bytes.Buffer
|
||||
err := successTemplate.ExecuteTemplate(&buf, "_", context)
|
||||
func build(payload *drone.Payload, context *Context) (string, string, string, error) {
|
||||
subject, err := template.RenderTrim(
|
||||
context.Vargs.Subject,
|
||||
payload)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
// generate the email subject
|
||||
var subject = fmt.Sprintf(
|
||||
Subject,
|
||||
context.Build.Status,
|
||||
context.Repo.Owner,
|
||||
context.Repo.Name,
|
||||
context.Build.Branch,
|
||||
context.Build.Commit[:8],
|
||||
html, err := template.RenderTrim(
|
||||
context.Vargs.Template,
|
||||
payload,
|
||||
)
|
||||
|
||||
return send(subject, buf.String(), context)
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
plain, err := html2text.FromString(
|
||||
html,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
return subject, plain, html, nil
|
||||
}
|
||||
|
||||
func send(subject, body string, c *Context) error {
|
||||
func send(subject, plainBody, htmlBody string, c *Context) error {
|
||||
if len(c.Vargs.Recipients) == 0 {
|
||||
c.Vargs.Recipients = []string{
|
||||
c.Build.Email,
|
||||
}
|
||||
}
|
||||
|
||||
var auth smtp.Auth
|
||||
m := gomail.NewMessage()
|
||||
|
||||
var addr = net.JoinHostPort(
|
||||
m.SetHeader(
|
||||
"To",
|
||||
c.Vargs.Recipients...,
|
||||
)
|
||||
|
||||
m.SetHeader(
|
||||
"From",
|
||||
c.Vargs.From,
|
||||
)
|
||||
|
||||
m.SetHeader(
|
||||
"Subject",
|
||||
subject,
|
||||
)
|
||||
|
||||
m.AddAlternative(
|
||||
"text/plain",
|
||||
plainBody,
|
||||
)
|
||||
|
||||
m.AddAlternative(
|
||||
"text/html",
|
||||
htmlBody,
|
||||
)
|
||||
|
||||
d := gomail.NewPlainDialer(
|
||||
c.Vargs.Host,
|
||||
strconv.Itoa(c.Vargs.Port))
|
||||
c.Vargs.Port,
|
||||
c.Vargs.Username,
|
||||
c.Vargs.Password,
|
||||
)
|
||||
|
||||
// setup the authentication to the smtp server
|
||||
// if the username and password are provided.
|
||||
if len(c.Vargs.Username) > 0 {
|
||||
auth = smtp.PlainAuth(
|
||||
"",
|
||||
c.Vargs.Username,
|
||||
c.Vargs.Password,
|
||||
c.Vargs.Host)
|
||||
if c.Vargs.SkipVerify {
|
||||
d.TLSConfig = &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
}
|
||||
|
||||
// genereate the raw email message
|
||||
var to = strings.Join(
|
||||
c.Vargs.Recipients,
|
||||
",")
|
||||
|
||||
var raw = fmt.Sprintf(
|
||||
rawMessage,
|
||||
c.Vargs.From,
|
||||
to,
|
||||
subject,
|
||||
body)
|
||||
|
||||
return smtp.SendMail(
|
||||
addr,
|
||||
auth,
|
||||
c.Vargs.From,
|
||||
c.Vargs.Recipients,
|
||||
[]byte(raw))
|
||||
return d.DialAndSend(m)
|
||||
}
|
||||
|
|
324
template.go
324
template.go
|
@ -1,41 +1,293 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
)
|
||||
var defaultSubject = `
|
||||
[{{ build.status }}] {{ repo.owner }}/{{ repo.name }} ({{ build.branch }} - {{ truncate build.commit 8 }})
|
||||
`
|
||||
|
||||
// raw email message template
|
||||
var rawMessage = `From: %s
|
||||
To: %s
|
||||
Subject: %s
|
||||
MIME-version: 1.0
|
||||
Content-Type: text/html; charset="UTF-8"
|
||||
%s`
|
||||
var defaultTemplate = `
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
|
||||
// default success email template
|
||||
var successTemplate = template.Must(template.New("_").Parse(`
|
||||
<p>
|
||||
<b>Build was Successful</b>
|
||||
(<a href="{{.System.Link}}/{{.Repo.Owner}}/{{.Repo.Name}}/{{.Build.Number}}">see results</a>)
|
||||
</p>
|
||||
<p>Repository: {{.Repo.Owner}}/{{.Repo.Name}}</p>
|
||||
<p>Commit: {{.Build.Commit}}</p>
|
||||
<p>Author: {{.Build.Author}}</p>
|
||||
<p>Branch: {{.Build.Branch}}</p>
|
||||
<p>Message:</p>
|
||||
<p>{{ .Build.Message }}</p>
|
||||
`))
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif;
|
||||
box-sizing: border-box;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
// default failure email template
|
||||
var failureTemplate = template.Must(template.New("_").Parse(`
|
||||
<p>
|
||||
<b>Build Failed</b>
|
||||
(<a href="{{.System.Link}}/{{.Repo.Owner}}/{{.Repo.Name}}/{{.Build.Number}}">see results</a>)
|
||||
</p>
|
||||
<p>Repository: {{.Repo.Owner}}/{{.Repo.Name}}</p>
|
||||
<p>Commit: {{.Build.Commit}}</p>
|
||||
<p>Author: {{.Build.Author}}</p>
|
||||
<p>Branch: {{.Build.Branch}}</p>
|
||||
<p>Message:</p>
|
||||
<p>{{ .Build.Message }}</p>
|
||||
`))
|
||||
body {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-webkit-text-size-adjust: none;
|
||||
width: 100% !important;
|
||||
height: 100%;
|
||||
line-height: 1.6;
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
|
||||
table td {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.body-wrap {
|
||||
background-color: #f6f6f6;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: block !important;
|
||||
max-width: 600px !important;
|
||||
margin: 0 auto !important;
|
||||
/* makes it centered */
|
||||
clear: both !important;
|
||||
}
|
||||
|
||||
.content {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.main {
|
||||
background: #fff;
|
||||
border: 1px solid #e9e9e9;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.content-wrap {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.content-block {
|
||||
padding: 0 0 20px;
|
||||
}
|
||||
|
||||
.header {
|
||||
width: 100%;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
h1, h2, h3 {
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
|
||||
color: #000;
|
||||
margin: 40px 0 0;
|
||||
line-height: 1.2;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 32px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 1px solid #e9e9e9;
|
||||
margin: 20px 0;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
p,
|
||||
ul,
|
||||
ol {
|
||||
margin-bottom: 10px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
p li,
|
||||
ul li,
|
||||
ol li {
|
||||
margin-left: 5px;
|
||||
list-style-position: inside;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #348eda;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.last {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.first {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.padding {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.aligncenter {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.alignright {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.alignleft {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.clear {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.alert {
|
||||
font-size: 16px;
|
||||
color: #fff;
|
||||
font-weight: 500;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
border-radius: 3px 3px 0 0;
|
||||
}
|
||||
|
||||
.alert a {
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.alert.alert-warning {
|
||||
background: #ff9f00;
|
||||
}
|
||||
|
||||
.alert.alert-bad {
|
||||
background: #d0021b;
|
||||
}
|
||||
|
||||
.alert.alert-good {
|
||||
background: #68b90f;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 640px) {
|
||||
h1,
|
||||
h2,
|
||||
h3 {
|
||||
font-weight: 600 !important;
|
||||
margin: 20px 0 5px !important;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 22px !important;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 18px !important;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 16px !important;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.content,
|
||||
.content-wrapper {
|
||||
padding: 10px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<table class="body-wrap">
|
||||
<tr>
|
||||
<td></td>
|
||||
<td class="container" width="600">
|
||||
<div class="content">
|
||||
<table class="main" width="100%" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
{{#success build.status}}
|
||||
<td class="alert alert-good">
|
||||
<a href="{{ system.link_url }}/{{ repo.owner }}/{{ repo.name }}/{{ build.number }}">
|
||||
Successful build #{{ build.number }}
|
||||
</a>
|
||||
</td>
|
||||
{{else}}
|
||||
<td class="alert alert-bad">
|
||||
<a href="{{ system.link_url }}/{{ repo.owner }}/{{ repo.name }}/{{ build.number }}">
|
||||
Failed build #{{ build.number }}
|
||||
</a>
|
||||
</td>
|
||||
{{/success}}
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="content-wrap">
|
||||
<table width="100%" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td>
|
||||
Repo:
|
||||
</td>
|
||||
<td>
|
||||
{{ repo.owner }}/{{ repo.name }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Author:
|
||||
</td>
|
||||
<td>
|
||||
{{ build.author }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Branch:
|
||||
</td>
|
||||
<td>
|
||||
{{ build.branch }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Commit:
|
||||
</td>
|
||||
<td>
|
||||
{{ truncate build.commit 8 }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Time:
|
||||
</td>
|
||||
<td>
|
||||
{{ duration build.started_at build.finished_at }}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr>
|
||||
<table width="100%" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td>
|
||||
{{ build.message }}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
|
Loading…
Reference in a new issue