Created
December 14, 2021 18:36
-
-
Save derat/fc5b16bff50ccb1380d3a603616c432a to your computer and use it in GitHub Desktop.
Patch https://github.com/GoogleCloudPlatform/cloud-build-notifiers at 2f9eebce378bf38964345d674e0969b9afe9ccbc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From 1c79a506deda796d6280b0648697bd4f2b1b181b Mon Sep 17 00:00:00 2001 | |
From: Daniel Erat <omitted> | |
Date: Sat, 11 Dec 2021 18:54:23 -0400 | |
Subject: [PATCH] Make SMTP notifier include additional info from build tags. | |
Update the SMTP notifier to include more details in the | |
email messages that it sends. Subjects now look like | |
"[project-id] trigger-name SUCCESS (short-build-id)", and | |
the table in the body also includes the trigger name, | |
commit, build ID, and start and end times. | |
The trigger name and commit don't appear to be included in | |
the Build protobuf that's sent over Pub/Sub | |
(https://pkg.go.dev/google.golang.org/genproto/googleapis/devtools/cloudbuild/v1#Build), | |
so they're passed via tags in the Cloud Build YAML | |
configuration, i.e. | |
tags: | |
- commit-$COMMIT_SHA | |
- trigger-$TRIGGER_NAME | |
A container that runs the modified notifier can be created | |
by symlinking smtp/Dockerfile into the repository root and | |
running a command like the following from there: | |
gcloud --project <project-id> builds submit \ | |
--tag gcr.io/<project-id>/smtp-notifier | |
setup.sh is also updated to allow a non-standard image to be | |
specified via the IMAGE_PATH environment variable, i.e. the | |
value passed to --tag in the previous command. For example: | |
CLOUDSDK_CORE_PROJECT=<project-id> \ | |
IMAGE_PATH=gcr.io/<project-id>/smtp-notifier:latest \ | |
./setup.sh smtp /path/to/smtp-notifier.yaml <secret-name> | |
--- | |
setup.sh | 4 +++- | |
smtp/main.go | 55 ++++++++++++++++++++++++++++++++++++++++++++--- | |
smtp/main_test.go | 32 ++++++++++++++++++++++++++- | |
3 files changed, 86 insertions(+), 5 deletions(-) | |
diff --git a/setup.sh b/setup.sh | |
index 60bb35b..e7b219e 100755 | |
--- a/setup.sh | |
+++ b/setup.sh | |
@@ -90,7 +90,9 @@ main() { | |
DESTINATION_BUCKET_NAME="${PROJECT_ID}-notifiers-config" | |
DESTINATION_BUCKET_URI="gs://${DESTINATION_BUCKET_NAME}" | |
DESTINATION_CONFIG_PATH="${DESTINATION_BUCKET_URI}/${SOURCE_CONFIG_BASENAME}" | |
- IMAGE_PATH="us-east1-docker.pkg.dev/gcb-release/cloud-build-notifiers/${NOTIFIER_TYPE}:latest" | |
+ if [ -z "$IMAGE_PATH" ]; then | |
+ IMAGE_PATH="us-east1-docker.pkg.dev/gcb-release/cloud-build-notifiers/${NOTIFIER_TYPE}:latest" | |
+ fi | |
SERVICE_NAME="${NOTIFIER_TYPE}-notifier" | |
SUBSCRIPTION_NAME="${NOTIFIER_TYPE}-subscription" | |
INVOKER_SA="cloud-run-pubsub-invoker@${PROJECT_ID}.iam.gserviceaccount.com" | |
diff --git a/smtp/main.go b/smtp/main.go | |
index ab6d163..9039b29 100644 | |
--- a/smtp/main.go | |
+++ b/smtp/main.go | |
@@ -22,11 +22,13 @@ import ( | |
"net/smtp" | |
"strings" | |
"text/template" | |
+ "time" | |
"github.com/GoogleCloudPlatform/cloud-build-notifiers/lib/notifiers" | |
log "github.com/golang/glog" | |
"github.com/golang/protobuf/proto" | |
cbpb "google.golang.org/genproto/googleapis/devtools/cloudbuild/v1" | |
+ tspb "google.golang.org/protobuf/types/known/timestamppb" | |
) | |
const ( | |
@@ -57,7 +59,7 @@ func (s *smtpNotifier) SetUp(ctx context.Context, cfg *notifiers.Config, sg noti | |
} | |
s.filter = prd | |
- tmpl, err := template.New("email_template").Parse(htmlBody) | |
+ tmpl, err := template.New("email_template").Funcs(templateFuncs).Parse(htmlBody) | |
if err != nil { | |
return fmt.Errorf("failed to parse HTML email template: %w", err) | |
} | |
@@ -158,6 +160,31 @@ func (s *smtpNotifier) sendSMTPNotification(build *cbpb.Build) error { | |
return nil | |
} | |
+// getTag tries to extract a value from build's tags. | |
+// Given a name "foo", it will look for a tag prefixed with "foo-" and return | |
+// the rest of the tag. If no matching tag is found, def is returned. | |
+// Tags apparently must be matched by "^[\\w][\\w.-]{0,127}$" (per the failure | |
+// message when you try to start a build with an invalid tag). | |
+func getTag(build *cbpb.Build, name, def string) string { | |
+ pre := name + "-" | |
+ for _, tag := range build.Tags { | |
+ if strings.HasPrefix(tag, pre) { | |
+ return tag[len(pre):] | |
+ } | |
+ } | |
+ return def | |
+} | |
+ | |
+// templateFuncs should be added before parsing the htmlBody template. | |
+var templateFuncs = template.FuncMap{ | |
+ "tag": func(build *cbpb.Build, name string) string { | |
+ return getTag(build, name, "") | |
+ }, | |
+ "time": func(ts tspb.Timestamp) string { | |
+ return ts.AsTime().Format(time.RFC1123Z) | |
+ }, | |
+} | |
+ | |
func (s *smtpNotifier) buildEmail(build *cbpb.Build) (string, error) { | |
logURL, err := notifiers.AddUTMParams(build.LogUrl, notifiers.EmailMedium) | |
if err != nil { | |
@@ -170,7 +197,9 @@ func (s *smtpNotifier) buildEmail(build *cbpb.Build) (string, error) { | |
return "", err | |
} | |
- subject := fmt.Sprintf("Cloud Build [%s]: %s", build.ProjectId, build.Id) | |
+ subject := fmt.Sprintf("[%s] %s %s (build %s)", | |
+ build.ProjectId, getTag(build, "trigger", "[unknown]"), | |
+ build.Status, strings.Split(build.Id, "-")[0]) | |
header := make(map[string]string) | |
if s.mcfg.from != s.mcfg.sender { | |
@@ -221,12 +250,32 @@ const htmlBody = `<!doctype html> | |
<div class="card-content white"> | |
<table class="bordered"> | |
<tbody> | |
+ <tr> | |
+ <td>Trigger</td> | |
+ <td>{{tag . "trigger"}}</td> | |
+ </tr> | |
+ <tr> | |
+ <td>Commit</td> | |
+ <td>{{tag . "commit"}}</td> | |
+ </tr> | |
+ <tr> | |
+ <td>Build</td> | |
+ <td>{{.Id}}</td> | |
+ </tr> | |
+ <tr> | |
+ <td>Start</td> | |
+ <td>{{time .StartTime}}</td> | |
+ </tr> | |
+ <tr> | |
+ <td>End</td> | |
+ <td>{{time .FinishTime}}</td> | |
+ </tr> | |
<tr> | |
<td>Status</td> | |
<td>{{.Status}}</td> | |
</tr> | |
<tr> | |
- <td>Log URL</td> | |
+ <td>Log</td> | |
<td><a href="{{.LogUrl}}">Click Here</a></td> | |
</tr> | |
</tbody> | |
diff --git a/smtp/main_test.go b/smtp/main_test.go | |
index 92d31c3..e0d73a3 100644 | |
--- a/smtp/main_test.go | |
+++ b/smtp/main_test.go | |
@@ -20,10 +20,12 @@ import ( | |
"strings" | |
"testing" | |
"text/template" | |
+ "time" | |
"github.com/GoogleCloudPlatform/cloud-build-notifiers/lib/notifiers" | |
"github.com/google/go-cmp/cmp" | |
cbpb "google.golang.org/genproto/googleapis/devtools/cloudbuild/v1" | |
+ tspb "google.golang.org/protobuf/types/known/timestamppb" | |
"gopkg.in/yaml.v2" | |
) | |
@@ -153,17 +155,32 @@ spec: | |
} | |
func TestDefaultEmailTemplate(t *testing.T) { | |
- tmpl, err := template.New("email_template").Parse(htmlBody) | |
+ tmpl, err := template.New("email_template").Funcs(templateFuncs).Parse(htmlBody) | |
if err != nil { | |
t.Fatalf("template.Parse failed: %v", err) | |
} | |
+ makeTimestamp := func(s string) *tspb.Timestamp { | |
+ tt, err := time.Parse(time.RFC3339, s) | |
+ if err != nil { | |
+ t.Fatalf("Failed parsing %q: %v", s, err) | |
+ } | |
+ return tspb.New(tt) | |
+ } | |
+ | |
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", | |
+ StartTime: makeTimestamp("2021-12-11T20:02:31Z"), | |
+ FinishTime: makeTimestamp("2021-12-11T20:04:51Z"), | |
+ Tags: []string{ | |
+ "trigger-some-trigger", | |
+ "branch-some-branch", | |
+ "commit-some-commit", | |
+ }, | |
} | |
body := new(bytes.Buffer) | |
@@ -182,4 +199,17 @@ func TestDefaultEmailTemplate(t *testing.T) { | |
if !strings.Contains(body.String(), `<a href="https://some.example.com/log/url">`) { | |
t.Error("missing Log URL") | |
} | |
+ | |
+ for _, tc := range []struct { | |
+ want, desc string | |
+ }{ | |
+ {`<td>some-trigger</td>`, "trigger"}, | |
+ {`<td>some-commit</td>`, "commit"}, | |
+ {`<td>Sat, 11 Dec 2021 20:02:31 +0000</td>`, "start"}, | |
+ {`<td>Sat, 11 Dec 2021 20:04:51 +0000</td>`, "finish"}, | |
+ } { | |
+ if !strings.Contains(body.String(), tc.want) { | |
+ t.Errorf("missing %v", tc.desc) | |
+ } | |
+ } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment