Skip to content

Commit 5b080d6

Browse files
authored
Merge pull request #201 from michaelact/smtp/subject-template
[SMTP] Customize Subject Email (by build template)
2 parents 6569f16 + 89522fa commit 5b080d6

File tree

3 files changed

+69
-14
lines changed

3 files changed

+69
-14
lines changed

smtp/main.go

+26-7
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ import (
1818
"bytes"
1919
"context"
2020
"fmt"
21-
"html/template"
21+
htmlTemplate "html/template"
22+
textTemplate "text/template"
2223
"mime/quotedprintable"
2324
"net/smtp"
2425
"strings"
@@ -41,15 +42,16 @@ func main() {
4142

4243
type smtpNotifier struct {
4344
filter notifiers.EventFilter
44-
tmpl *template.Template
45+
htmlTmpl *htmlTemplate.Template
46+
textTmpl *textTemplate.Template
4547
mcfg mailConfig
4648
br notifiers.BindingResolver
4749
tmplView *notifiers.TemplateView
4850
}
4951

5052
type mailConfig struct {
51-
server, port, sender, from, password string
52-
recipients []string
53+
server, port, sender, from, password, subject string
54+
recipients []string
5355
}
5456

5557
func (s *smtpNotifier) SetUp(ctx context.Context, cfg *notifiers.Config, cfgTemplate string, sg notifiers.SecretGetter, br notifiers.BindingResolver) error {
@@ -58,11 +60,19 @@ func (s *smtpNotifier) SetUp(ctx context.Context, cfg *notifiers.Config, cfgTemp
5860
return fmt.Errorf("failed to create CELPredicate: %w", err)
5961
}
6062
s.filter = prd
61-
tmpl, err := template.New("email_template").Parse(cfgTemplate)
63+
htmlTmpl, err := htmlTemplate.New("email_template").Parse(cfgTemplate)
6264
if err != nil {
6365
return fmt.Errorf("failed to parse HTML email template: %w", err)
6466
}
65-
s.tmpl = tmpl
67+
s.htmlTmpl = htmlTmpl
68+
69+
if subject, subjectFound := cfg.Spec.Notification.Delivery["subject"]; subjectFound {
70+
textTmpl, err := textTemplate.New("subject_template").Parse(subject.(string))
71+
if err != nil {
72+
return fmt.Errorf("failed to parse TEXT subject template: %w", err)
73+
}
74+
s.textTmpl = textTmpl
75+
}
6676

6777
mcfg, err := getMailConfig(ctx, sg, cfg.Spec)
6878
if err != nil {
@@ -175,11 +185,20 @@ func (s *smtpNotifier) buildEmail() (string, error) {
175185
build.LogUrl = logURL
176186

177187
body := new(bytes.Buffer)
178-
if err := s.tmpl.Execute(body, s.tmplView); err != nil {
188+
if err := s.htmlTmpl.Execute(body, s.tmplView); err != nil {
179189
return "", err
180190
}
181191

182192
subject := fmt.Sprintf("Cloud Build [%s]: %s", build.ProjectId, build.Id)
193+
if s.textTmpl != nil {
194+
subjectTmpl := new(bytes.Buffer)
195+
if err := s.textTmpl.Execute(subjectTmpl, s.tmplView); err != nil {
196+
return "", err
197+
}
198+
199+
// Escape any string formatter
200+
subject = strings.Join(strings.Fields(subjectTmpl.String()), " ")
201+
}
183202

184203
header := make(map[string]string)
185204
if s.mcfg.from != s.mcfg.sender {

smtp/main_test.go

+42-7
Original file line numberDiff line numberDiff line change
@@ -47,16 +47,16 @@ const htmlBody = `<!doctype html>
4747
</div>
4848
<div class="card-content white">
4949
<table class="bordered">
50-
<tbody>
50+
<tbody>
5151
<tr>
52-
<td>Status</td>
53-
<td>{{.Params.buildStatus}}</td>
52+
<td>Status</td>
53+
<td>{{.Params.buildStatus}}</td>
5454
</tr>
5555
<tr>
56-
<td>Log URL</td>
57-
<td><a href="{{.Build.LogUrl}}">Click Here</a></td>
56+
<td>Log URL</td>
57+
<td><a href="{{.Build.LogUrl}}">Click Here</a></td>
5858
</tr>
59-
</tbody>
59+
</tbody>
6060
</table>
6161
</div>
6262
</div>
@@ -66,6 +66,8 @@ const htmlBody = `<!doctype html>
6666
</div>
6767
</html>`
6868

69+
const templateSubject = `Build {{.Build.Id}} Status: {{.Build.Status}}`
70+
6971
type fakeSecretGetter struct{}
7072

7173
func (f *fakeSecretGetter) GetSecret(_ context.Context, _ string) (string, error) {
@@ -147,7 +149,7 @@ metadata:
147149
name: failed-build-email-notification
148150
spec:
149151
notification:
150-
filter: event.buildTriggerStatus == STATUS_FAILED
152+
filter: event.buildTriggerStatus == "STATUS_FAILED"
151153
delivery:
152154
server: smtp.example.com
153155
port: '587'
@@ -194,6 +196,7 @@ func TestDefaultEmailTemplate(t *testing.T) {
194196
if err != nil {
195197
t.Fatalf("template.Parse failed: %v", err)
196198
}
199+
197200
build := &cbpb.Build{
198201
Id: "some-build-id",
199202
ProjectId: "my-project-id",
@@ -226,3 +229,35 @@ func TestDefaultEmailTemplate(t *testing.T) {
226229
t.Error("missing Log URL")
227230
}
228231
}
232+
233+
func TestSubjectEmailTemplate(t *testing.T) {
234+
tmpl, err := template.New("subject_template").Parse(templateSubject)
235+
if err != nil {
236+
t.Fatalf("failed to parse subject template: %v", err)
237+
}
238+
239+
build := &cbpb.Build{
240+
Id: "some-build-id",
241+
ProjectId: "my-project-id",
242+
BuildTriggerId: "some-trigger-id",
243+
Status: cbpb.Build_SUCCESS,
244+
LogUrl: "https://some.example.com/log/url",
245+
}
246+
247+
view := &notifiers.TemplateView{
248+
Build: &notifiers.BuildView{
249+
Build: build,
250+
},
251+
Params: map[string]string{"buildStatus": "SUCCESS"},
252+
}
253+
254+
subject := new(bytes.Buffer)
255+
if err := tmpl.Execute(subject, view); err != nil {
256+
t.Fatalf("failed to execute subject template: %v", err)
257+
}
258+
259+
expectedSubject := "Build some-build-id Status: SUCCESS"
260+
if subject.String() != expectedSubject {
261+
t.Errorf("expected subject %q, but got %q", expectedSubject, subject.String())
262+
}
263+
}

smtp/smtp.yaml.example

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ spec:
4848
delivery:
4949
server: smtp.gmail.com
5050
port: '587'
51+
subject: '{{ if eq .Build.Status 3 }}✅ Build Successful ({{ .Build.Substitutions.REPO_NAME }}){{ else }}❌ Build Failure ({{ .Build.Substitutions.REPO_NAME }}){{ end }} | {{ if .Build.Substitutions._COMMIT_MESSAGE }} {{ if gt (len .Build.Substitutions._COMMIT_MESSAGE) 100 }}{{ slice .Build.Substitutions._COMMIT_MESSAGE 0 100 }}{{ else }}{{ .Build.Substitutions._COMMIT_MESSAGE }}{{ end }}{{ else }}{{ .Build.Substitutions.COMMIT_SHA }}{{ end }} [...]'
5152
5253
5354
recipients:

0 commit comments

Comments
 (0)