diff --git a/smtp/main.go b/smtp/main.go index dd49c87f..568bc685 100644 --- a/smtp/main.go +++ b/smtp/main.go @@ -18,7 +18,8 @@ import ( "bytes" "context" "fmt" - "html/template" + htmlTemplate "html/template" + textTemplate "text/template" "mime/quotedprintable" "net/smtp" "strings" @@ -41,15 +42,16 @@ func main() { type smtpNotifier struct { filter notifiers.EventFilter - tmpl *template.Template + htmlTmpl *htmlTemplate.Template + textTmpl *textTemplate.Template mcfg mailConfig br notifiers.BindingResolver tmplView *notifiers.TemplateView } type mailConfig struct { - server, port, sender, from, password string - recipients []string + server, port, sender, from, password, subject string + recipients []string } 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 return fmt.Errorf("failed to create CELPredicate: %w", err) } s.filter = prd - tmpl, err := template.New("email_template").Parse(cfgTemplate) + htmlTmpl, err := htmlTemplate.New("email_template").Parse(cfgTemplate) if err != nil { return fmt.Errorf("failed to parse HTML email template: %w", err) } - s.tmpl = tmpl + s.htmlTmpl = htmlTmpl + + if subject, subjectFound := cfg.Spec.Notification.Delivery["subject"]; subjectFound { + textTmpl, err := textTemplate.New("subject_template").Parse(subject.(string)) + if err != nil { + return fmt.Errorf("failed to parse TEXT subject template: %w", err) + } + s.textTmpl = textTmpl + } mcfg, err := getMailConfig(ctx, sg, cfg.Spec) if err != nil { @@ -175,11 +185,20 @@ func (s *smtpNotifier) buildEmail() (string, error) { build.LogUrl = logURL body := new(bytes.Buffer) - if err := s.tmpl.Execute(body, s.tmplView); err != nil { + if err := s.htmlTmpl.Execute(body, s.tmplView); err != nil { return "", err } subject := fmt.Sprintf("Cloud Build [%s]: %s", build.ProjectId, build.Id) + if s.textTmpl != nil { + subjectTmpl := new(bytes.Buffer) + if err := s.textTmpl.Execute(subjectTmpl, s.tmplView); err != nil { + return "", err + } + + // Escape any string formatter + subject = strings.Join(strings.Fields(subjectTmpl.String()), " ") + } header := make(map[string]string) if s.mcfg.from != s.mcfg.sender { diff --git a/smtp/main_test.go b/smtp/main_test.go index 229349b2..959e2223 100644 --- a/smtp/main_test.go +++ b/smtp/main_test.go @@ -47,16 +47,16 @@ const htmlBody = `
- + - - + + - - + + - +
Status{{.Params.buildStatus}}Status{{.Params.buildStatus}}
Log URLClick HereLog URLClick Here
@@ -66,6 +66,8 @@ const htmlBody = ` ` +const templateSubject = `Build {{.Build.Id}} Status: {{.Build.Status}}` + type fakeSecretGetter struct{} func (f *fakeSecretGetter) GetSecret(_ context.Context, _ string) (string, error) { @@ -147,7 +149,7 @@ metadata: name: failed-build-email-notification spec: notification: - filter: event.buildTriggerStatus == “STATUS_FAILED” + filter: event.buildTriggerStatus == "STATUS_FAILED" delivery: server: smtp.example.com port: '587' @@ -194,6 +196,7 @@ func TestDefaultEmailTemplate(t *testing.T) { if err != nil { t.Fatalf("template.Parse failed: %v", err) } + build := &cbpb.Build{ Id: "some-build-id", ProjectId: "my-project-id", @@ -226,3 +229,35 @@ func TestDefaultEmailTemplate(t *testing.T) { t.Error("missing Log URL") } } + +func TestSubjectEmailTemplate(t *testing.T) { + tmpl, err := template.New("subject_template").Parse(templateSubject) + if err != nil { + t.Fatalf("failed to parse subject template: %v", err) + } + + build := &cbpb.Build{ + Id: "some-build-id", + ProjectId: "my-project-id", + BuildTriggerId: "some-trigger-id", + Status: cbpb.Build_SUCCESS, + LogUrl: "https://some.example.com/log/url", + } + + view := ¬ifiers.TemplateView{ + Build: ¬ifiers.BuildView{ + Build: build, + }, + Params: map[string]string{"buildStatus": "SUCCESS"}, + } + + subject := new(bytes.Buffer) + if err := tmpl.Execute(subject, view); err != nil { + t.Fatalf("failed to execute subject template: %v", err) + } + + expectedSubject := "Build some-build-id Status: SUCCESS" + if subject.String() != expectedSubject { + t.Errorf("expected subject %q, but got %q", expectedSubject, subject.String()) + } +} diff --git a/smtp/smtp.yaml.example b/smtp/smtp.yaml.example index 05bc859b..ced70dd0 100644 --- a/smtp/smtp.yaml.example +++ b/smtp/smtp.yaml.example @@ -48,6 +48,7 @@ spec: delivery: server: smtp.gmail.com port: '587' + 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 }} [...]' sender: example-sender@gmail.com from: example-from@gmail.com recipients: