[gold] Use proper templates for CL messages

This allows us to easily change Chrome's message to link to the public
repo.

Bug: skia:10442
Change-Id: I8f887766ed21eef06621db6e388ef0c23ce9ae52
Reviewed-on: https://skia-review.googlesource.com/c/buildbot/+/301954
Reviewed-by: Leandro Lovisolo <lovisolo@google.com>
diff --git a/golden/cmd/skiacorrectness/main.go b/golden/cmd/skiacorrectness/main.go
index 4c36f2b..fded6ff 100644
--- a/golden/cmd/skiacorrectness/main.go
+++ b/golden/cmd/skiacorrectness/main.go
@@ -79,12 +79,8 @@
 	// A list of email addresses or domains that can log into this instance.
 	AuthorizedUsers []string `json:"authorized_users"`
 
-	// A string with five placeholders that will be used to make a comment. Those placeholders are
-	//  - number of untriaged digests (int)
-	//  - affected PatchSet order (int)
-	//  - gold instance url (string)
-	//  - code review system (string)
-	//  - affected ChangeList id (string)
+	// A string with placeholders for generating a comment message. See
+	// commenter.commentTemplateContext for the exact fields.
 	CLCommentTemplate string `json:"cl_comment_template"`
 
 	// Client secret file for OAuth2 authentication.
@@ -151,6 +147,9 @@
 	// this instance.
 	PubliclyAllowableParams publicparams.MatchingRules `json:"publicly_allowed_params" optional:"true"`
 
+	// This can be used in a CL comment to direct users to the public instance for triaging.
+	PublicSiteURL string `json:"public_site_url" optional:"true"`
+
 	// The path to the directory that contains Polymer templates, JS, and CSS files.
 	ResourcesPath string `json:"resources_path"`
 
@@ -428,7 +427,10 @@
 	sklog.Infof("Search API created")
 
 	if isAuthoritative && !fsc.DisableCLTracking {
-		clCommenter := commenter.New(crs, cls, searchAPI, fsc.CLCommentTemplate, fsc.SiteURL, fsc.DisableCLComments)
+		clCommenter, err := commenter.New(crs, cls, searchAPI, fsc.CLCommentTemplate, fsc.SiteURL, fsc.PublicSiteURL, fsc.DisableCLComments)
+		if err != nil {
+			sklog.Fatalf("Could not initialize commenter: %s", err)
+		}
 		startCommenter(ctx, clCommenter)
 	}
 
diff --git a/golden/go/code_review/commenter/commenter.go b/golden/go/code_review/commenter/commenter.go
index 8878a7a..e0e203d 100644
--- a/golden/go/code_review/commenter/commenter.go
+++ b/golden/go/code_review/commenter/commenter.go
@@ -3,9 +3,10 @@
 package commenter
 
 import (
+	"bytes"
 	"context"
-	"fmt"
 	"sync"
+	"text/template"
 	"time"
 
 	"go.skia.org/infra/go/metrics2"
@@ -29,8 +30,9 @@
 	crs             code_review.Client
 	store           clstore.Store
 	instanceURL     string
+	publicURL       string
 	logCommentsOnly bool
-	messageTemplate string
+	messageTemplate *template.Template
 	search          search.SearchAPI
 
 	liveness metrics2.Liveness
@@ -38,17 +40,22 @@
 	now func() time.Time
 }
 
-func New(c code_review.Client, s clstore.Store, search search.SearchAPI, messageTemplate, instanceURL string, logCommentsOnly bool) *Impl {
+func New(c code_review.Client, s clstore.Store, search search.SearchAPI, messageTemplate, instanceURL, publicURL string, logCommentsOnly bool) (*Impl, error) {
+	templ, err := template.New("message").Parse(messageTemplate)
+	if err != nil && messageTemplate != "" {
+		return nil, skerr.Wrapf(err, "Message template %q", messageTemplate)
+	}
 	return &Impl{
 		crs:             c,
 		store:           s,
 		instanceURL:     instanceURL,
+		publicURL:       publicURL,
 		logCommentsOnly: logCommentsOnly,
-		messageTemplate: messageTemplate,
+		messageTemplate: templ,
 		liveness:        metrics2.NewLiveness(completedCommentCycle),
 		search:          search,
 		now:             time.Now,
-	}
+	}, nil
 }
 
 // CommentOnChangeListsWithUntriagedDigests implements the code_review.ChangeListCommenter
@@ -163,11 +170,20 @@
 // logs if this commenter is configured to not actually comment.
 func (i *Impl) maybeCommentOn(ctx context.Context, cl code_review.ChangeList, ps code_review.PatchSet, untriagedDigests int) error {
 	crs := i.crs.System()
+	msg, err := i.untriagedMessage(commentTemplateContext{
+		CRS:           crs,
+		ChangeListID:  cl.SystemID,
+		PatchSetOrder: ps.Order,
+		NumUntriaged:  untriagedDigests,
+	})
+	if err != nil {
+		return skerr.Wrap(err)
+	}
 	if i.logCommentsOnly {
-		sklog.Infof("Should comment on CL %s with message %s", cl.SystemID, i.untriagedMessage(crs, cl, ps, untriagedDigests))
+		sklog.Infof("Should comment on CL %s with message %s", cl.SystemID, msg)
 		return nil
 	}
-	if err := i.crs.CommentOn(ctx, cl.SystemID, i.untriagedMessage(crs, cl, ps, untriagedDigests)); err != nil {
+	if err := i.crs.CommentOn(ctx, cl.SystemID, msg); err != nil {
 		if err == code_review.ErrNotFound {
 			sklog.Warningf("Cannot comment on %s CL %s because it does not exist", i.crs.System(), cl.SystemID)
 			return nil
@@ -177,9 +193,25 @@
 	return nil
 }
 
+// commentTemplateContext contains the fields that can be substituted into
+type commentTemplateContext struct {
+	ChangeListID      string
+	CRS               string
+	InstanceURL       string
+	NumUntriaged      int
+	PatchSetOrder     int
+	PublicInstanceURL string
+}
+
 // untriagedMessage returns a message about untriaged images on the given CL/PS.
-func (i *Impl) untriagedMessage(crs string, cl code_review.ChangeList, ps code_review.PatchSet, untriagedDigests int) string {
-	return fmt.Sprintf(i.messageTemplate, untriagedDigests, ps.Order, i.instanceURL, crs, cl.SystemID)
+func (i *Impl) untriagedMessage(c commentTemplateContext) (string, error) {
+	c.InstanceURL = i.instanceURL
+	c.PublicInstanceURL = i.publicURL
+	var b bytes.Buffer
+	if err := i.messageTemplate.Execute(&b, c); err != nil {
+		return "", skerr.Wrapf(err, "With template context %#v", c)
+	}
+	return b.String(), nil
 }
 
 // updateCLInStoreIfAbandoned checks with the CRS to see if the cl is still Open. If it is, it
diff --git a/golden/go/code_review/commenter/commenter_test.go b/golden/go/code_review/commenter/commenter_test.go
index 31313a6..5bf254d 100644
--- a/golden/go/code_review/commenter/commenter_test.go
+++ b/golden/go/code_review/commenter/commenter_test.go
@@ -74,7 +74,7 @@
 	})
 	mcs.On("PutChangeList", testutils.AnyContext, putClMatcher).Return(nil).Once()
 
-	c := newTestCommenter(mcr, mcs, nil)
+	c := newTestCommenter(t, mcr, mcs, nil)
 	err := c.CommentOnChangeListsWithUntriagedDigests(context.Background())
 	require.NoError(t, err)
 	assert.Equal(t, "8", metrics_utils.GetRecordedMetric(t, numRecentOpenCLsMetric, nil))
@@ -109,7 +109,7 @@
 		return xcl[i]
 	}, nil)
 
-	c := newTestCommenter(mcr, mcs, nil)
+	c := newTestCommenter(t, mcr, mcs, nil)
 	err := c.CommentOnChangeListsWithUntriagedDigests(context.Background())
 	require.NoError(t, err)
 	// The one CL that was not found should not be counted as an open CL.
@@ -128,7 +128,7 @@
 	mcs.On("GetChangeLists", testutils.AnyContext, mock.Anything).Return(makeChangeLists(5), 5, nil)
 	mcr.On("GetChangeList", testutils.AnyContext, mock.Anything).Return(code_review.ChangeList{}, code_review.ErrNotFound)
 
-	c := newTestCommenter(mcr, mcs, nil)
+	c := newTestCommenter(t, mcr, mcs, nil)
 	err := c.CommentOnChangeListsWithUntriagedDigests(context.Background())
 	require.NoError(t, err)
 	// This shouldn't hang and we'll see 0 open CLs
@@ -154,7 +154,7 @@
 	mcr.On("GetChangeList", testutils.AnyContext, mock.Anything).Return(code_review.ChangeList{}, errors.New("GitHub down"))
 	mcr.On("System").Return("github")
 
-	c := newTestCommenter(mcr, mcs, nil)
+	c := newTestCommenter(t, mcr, mcs, nil)
 	err := c.CommentOnChangeListsWithUntriagedDigests(context.Background())
 	assertErrorWasCanceledOrContains(t, err, "down", "github")
 }
@@ -182,7 +182,7 @@
 
 	mcs.On("PutChangeList", testutils.AnyContext, mock.Anything).Return(errors.New("firestore broke"))
 
-	c := newTestCommenter(mcr, mcs, nil)
+	c := newTestCommenter(t, mcr, mcs, nil)
 	err := c.CommentOnChangeListsWithUntriagedDigests(context.Background())
 	assertErrorWasCanceledOrContains(t, err, "firestore broke")
 }
@@ -237,6 +237,7 @@
 			assert.Fail(t, "unexpected call")
 		}
 		assert.Contains(t, msg, "2 untriaged digest(s)")
+		assert.Contains(t, msg, publicInstanceURL)
 		return nil
 	}, nil)
 	mcr.On("System").Return("github")
@@ -247,7 +248,7 @@
 		TS:      indexTime,
 	}, nil)
 
-	c := newTestCommenter(mcr, mcs, msa)
+	c := newTestCommenter(t, mcr, mcs, msa)
 	err := c.CommentOnChangeListsWithUntriagedDigests(context.Background())
 	require.NoError(t, err)
 }
@@ -294,7 +295,7 @@
 		TS:      indexTime,
 	}, nil)
 
-	c := newTestCommenter(mcr, mcs, msa)
+	c := newTestCommenter(t, mcr, mcs, msa)
 	err := c.CommentOnChangeListsWithUntriagedDigests(context.Background())
 	require.NoError(t, err)
 }
@@ -333,7 +334,7 @@
 	// Simulate an error working with the
 	msa.On("UntriagedUnignoredTryJobExclusiveDigests", testutils.AnyContext, mock.Anything).Return(nil, errors.New("boom"))
 
-	c := newTestCommenter(mcr, mcs, msa)
+	c := newTestCommenter(t, mcr, mcs, msa)
 	err := c.CommentOnChangeListsWithUntriagedDigests(context.Background())
 	require.NoError(t, err)
 }
@@ -376,7 +377,7 @@
 		Digests: []types.Digest{"doesn't", "matter"},
 	}, nil)
 
-	c := newTestCommenter(mcr, mcs, msa)
+	c := newTestCommenter(t, mcr, mcs, msa)
 	c.logCommentsOnly = true
 	err := c.CommentOnChangeListsWithUntriagedDigests(context.Background())
 	require.NoError(t, err)
@@ -404,7 +405,7 @@
 	}, nil)
 	// no calls to CommentOn expected because the CL has already been commented on
 
-	c := newTestCommenter(mcr, mcs, nil)
+	c := newTestCommenter(t, mcr, mcs, nil)
 	err := c.CommentOnChangeListsWithUntriagedDigests(context.Background())
 	require.NoError(t, err)
 }
@@ -428,7 +429,7 @@
 		return xcl[i]
 	}, nil)
 
-	c := newTestCommenter(mcr, mcs, nil)
+	c := newTestCommenter(t, mcr, mcs, nil)
 	err := c.CommentOnChangeListsWithUntriagedDigests(context.Background())
 	require.Error(t, err)
 	assert.Contains(t, err.Error(), "firestore kaput")
@@ -462,7 +463,7 @@
 		Digests: []types.Digest{"doesn't", "matter"},
 	}, nil)
 
-	c := newTestCommenter(mcr, mcs, msa)
+	c := newTestCommenter(t, mcr, mcs, msa)
 	err := c.CommentOnChangeListsWithUntriagedDigests(context.Background())
 	assertErrorWasCanceledOrContains(t, err, "internet down")
 }
@@ -504,7 +505,7 @@
 		Digests: []types.Digest{"doesn't", "matter"},
 	}, nil)
 
-	c := newTestCommenter(mcr, mcs, msa)
+	c := newTestCommenter(t, mcr, mcs, msa)
 	err := c.CommentOnChangeListsWithUntriagedDigests(context.Background())
 	require.NoError(t, err)
 }
@@ -557,8 +558,9 @@
 	}
 }
 
-func newTestCommenter(mcr *mock_codereview.Client, mcs *mock_clstore.Store, msa *mock_search.SearchAPI) *Impl {
-	c := New(mcr, mcs, msa, basicTemplate, instanceURL, false)
+func newTestCommenter(t *testing.T, mcr *mock_codereview.Client, mcs *mock_clstore.Store, msa *mock_search.SearchAPI) *Impl {
+	c, err := New(mcr, mcs, msa, basicTemplate, instanceURL, publicInstanceURL, false)
+	require.NoError(t, err)
 	c.now = func() time.Time {
 		return fakeNow
 	}
@@ -566,9 +568,11 @@
 }
 
 const (
-	instanceURL   = "gold.skia.org"
-	basicTemplate = `Gold has detected about %d untriaged digest(s) on patchset %d.
-Please triage them at %s/cl/%s/%s.`
+	instanceURL       = "gold.skia.org"
+	publicInstanceURL = "public-gold.skia.org"
+	basicTemplate     = `Gold has detected about {{.NumUntriaged}} untriaged digest(s) on patchset {{.PatchSetOrder}}.
+Please triage them at {{.InstanceURL}}/cl/{{.CRS}}/{{.ChangeListID}}.
+The public instance is {{.PublicInstanceURL}}.`
 )
 
 var (
diff --git a/golden/k8s-config-templates/gold-common.json5 b/golden/k8s-config-templates/gold-common.json5
index 1cd0b46..bc801c2 100644
--- a/golden/k8s-config-templates/gold-common.json5
+++ b/golden/k8s-config-templates/gold-common.json5
@@ -2,7 +2,7 @@
     "BASELINE_SERVER_IMAGE": "gcr.io/skia-public/gold-baseline-server:2020-06-24T12_35_34Z-kjlubick-5535579-clean",
     "DIFF_SERVER_IMAGE":     "gcr.io/skia-public/gold-diff-server:2020-06-29T16_32_35Z-kjlubick-dba6b28-clean",
     "INGESTION_BT_IMAGE":    "gcr.io/skia-public/gold-ingestion:2020-06-29T18_25_07Z-kjlubick-4c0fef1-clean",
-    "SKIACORRECTNESS_IMAGE": "gcr.io/skia-public/gold-skiacorrectness:2020-07-06T11_35_07Z-kjlubick-d75a88d-clean",
+    "SKIACORRECTNESS_IMAGE": "gcr.io/skia-public/gold-skiacorrectness:2020-07-10T19_42_46Z-kjlubick-9477ae4-dirty",
 
     // Services for testing goldpushk.
     "HEALTHY_SERVER_IMAGE": "gcr.io/skia-public/goldpushk-healthy-server:2019-10-16T18_39_09Z-lovisolo-9b77357-clean",
diff --git a/golden/k8s-instances/chrome-gpu/chrome-gpu-skiacorrectness.json5 b/golden/k8s-instances/chrome-gpu/chrome-gpu-skiacorrectness.json5
index e518f05..0bd2cf9 100644
--- a/golden/k8s-instances/chrome-gpu/chrome-gpu-skiacorrectness.json5
+++ b/golden/k8s-instances/chrome-gpu/chrome-gpu-skiacorrectness.json5
@@ -2,9 +2,9 @@
   authorized_users: [
     "google.com", "chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com",
   ],
-  cl_comment_template: "Gold has detected about %d untriaged digest(s) on patchset %d.\n\
+  cl_comment_template: "Gold has detected about {{.NumUntriaged}} untriaged digest(s) on patchset {{.PatchSetOrder}}.\n\
 \n\
-Please triage them at %s/cl/%s/%s before submitting.\n\
+Please triage them at {{.InstanceURL}}/cl/{{.CRS}}/{{.ChangeListID}} before submitting.\n\
 \n\
 If this is due to a failure in a gtest-based test, this functionality is currently experimental and won't block your CL.\n\
 If all the trybots passed and you don't expect your CL to have any effect on browser UI, you can likely ignore this message.\n\
diff --git a/golden/k8s-instances/chrome/chrome-skiacorrectness.json5 b/golden/k8s-instances/chrome/chrome-skiacorrectness.json5
index b390956..2f81325 100644
--- a/golden/k8s-instances/chrome/chrome-skiacorrectness.json5
+++ b/golden/k8s-instances/chrome/chrome-skiacorrectness.json5
@@ -3,9 +3,11 @@
     "google.com", "chromium.org", "microsoft.com",
     "chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com",
   ],
-  cl_comment_template: "Gold has detected about %d untriaged digest(s) on patchset %d.\n\
+  cl_comment_template: "Gold has detected about {{.NumUntriaged}} untriaged digest(s) on patchset {{.PatchSetOrder}}.\n\
+Please triage them using one of the following links before submitting:\n\
 \n\
-Please triage them at %s/cl/%s/%s before submitting.\n\
+  - {{.InstanceURL}}/cl/{{.CRS}}/{{.ChangeListID}} if you are working on an internal repo or otherwise expect some test results to be non-public.\n\
+  - {{.PublicInstanceURL}}/cl/{{.CRS}}/{{.ChangeListID}} otherwise.\n\
 \n\
 If this is due to a failure in a gtest-based test, this functionality is currently experimental and won't block your CL.\n\
 If all the trybots passed and you don't expect your CL to have any effect on browser UI, you can likely ignore this message.\n\
@@ -26,6 +28,7 @@
   num_commits: 512,
   port: ":8000",
   prom_port: ":20000",
+  public_site_url: "https://chrome-public-gold.skia.org",
   resources_path: "/usr/local/share/skiacorrectness/frontend",
   site_url: "https://chrome-gold.skia.org",
   tile_freshness: "1m",
diff --git a/golden/k8s-instances/flutter-engine/flutter-engine-skiacorrectness.json5 b/golden/k8s-instances/flutter-engine/flutter-engine-skiacorrectness.json5
index d422478..7470bba 100644
--- a/golden/k8s-instances/flutter-engine/flutter-engine-skiacorrectness.json5
+++ b/golden/k8s-instances/flutter-engine/flutter-engine-skiacorrectness.json5
@@ -2,8 +2,8 @@
   authorized_users: [
     "google.com",
   ],
-  cl_comment_template: "Gold has detected about %d untriaged digest(s) on patchset %d. \
-View them at %s/cl/%s/%s",
+  cl_comment_template: "Gold has detected about {{.NumUntriaged}} untriaged digest(s) on patchset {{.PatchSetOrder}}.\n\
+View them at {{.InstanceURL}}/cl/{{.CRS}}/{{.ChangeListID}}",
   client_secret_file: "/etc/skia.org/login.json",
   crs_url_template: "https://github.com/flutter/engine/pull/%s",
   diff_server_grpc: "gold-flutter-engine-diffserver:8000",
diff --git a/golden/k8s-instances/flutter/flutter-skiacorrectness.json5 b/golden/k8s-instances/flutter/flutter-skiacorrectness.json5
index c9c8f9a..86b44d3 100644
--- a/golden/k8s-instances/flutter/flutter-skiacorrectness.json5
+++ b/golden/k8s-instances/flutter/flutter-skiacorrectness.json5
@@ -2,8 +2,8 @@
   authorized_users: [
     "google.com",
   ],
-  cl_comment_template: "Gold has detected about %d untriaged digest(s) on patchset %d. \
-View them at %s/cl/%s/%s",
+  cl_comment_template: "Gold has detected about {{.NumUntriaged}} untriaged digest(s) on patchset {{.PatchSetOrder}}.\n\
+View them at {{.InstanceURL}}/cl/{{.CRS}}/{{.ChangeListID}}",
   client_secret_file: "/etc/skia.org/login.json",
   crs_url_template: "https://github.com/flutter/flutter/pull/%s",
   diff_server_grpc: "gold-flutter-diffserver:8000",
diff --git a/golden/k8s-instances/fuchsia-public/fuchsia-public-skiacorrectness.json5 b/golden/k8s-instances/fuchsia-public/fuchsia-public-skiacorrectness.json5
index fee1e6a..4d15073 100644
--- a/golden/k8s-instances/fuchsia-public/fuchsia-public-skiacorrectness.json5
+++ b/golden/k8s-instances/fuchsia-public/fuchsia-public-skiacorrectness.json5
@@ -2,8 +2,8 @@
   authorized_users: [
     "google.com",
   ],
-  cl_comment_template: "Gold has detected about %d untriaged digest(s) on patchset %d. \
-View them at %s/cl/%s/%s",
+  cl_comment_template: "Gold has detected about {{.NumUntriaged}} untriaged digest(s) on patchset {{.PatchSetOrder}}.\n\
+View them at {{.InstanceURL}}/cl/{{.CRS}}/{{.ChangeListID}}",
   client_secret_file: "/etc/skia.org/login.json",
   crs_url_template: "https://fuchsia-review.googlesource.com/%s",
   diff_server_grpc: "gold-fuchsia-public-diffserver:8000",
diff --git a/golden/k8s-instances/lottie/lottie-skiacorrectness.json5 b/golden/k8s-instances/lottie/lottie-skiacorrectness.json5
index bc8c477..9db377e 100644
--- a/golden/k8s-instances/lottie/lottie-skiacorrectness.json5
+++ b/golden/k8s-instances/lottie/lottie-skiacorrectness.json5
@@ -2,8 +2,8 @@
   authorized_users: [
     "google.com", "chromium.org", "skia.org",
   ],
-  cl_comment_template: "Gold has detected about %d untriaged digest(s) on patchset %d. \
-View them at %s/cl/%s/%s",
+  cl_comment_template: "Gold has detected about {{.NumUntriaged}} untriaged digest(s) on patchset {{.PatchSetOrder}}.\n\
+View them at {{.InstanceURL}}/cl/{{.CRS}}/{{.ChangeListID}}",
   client_secret_file: "/etc/skia.org/login.json",
   crs_url_template: "https://skia-review.googlesource.com/%s",
   diff_server_grpc: "gold-lottie-diffserver:8000",
diff --git a/golden/k8s-instances/pdfium/pdfium-skiacorrectness.json5 b/golden/k8s-instances/pdfium/pdfium-skiacorrectness.json5
index ddf2c59..cfa6374 100644
--- a/golden/k8s-instances/pdfium/pdfium-skiacorrectness.json5
+++ b/golden/k8s-instances/pdfium/pdfium-skiacorrectness.json5
@@ -2,8 +2,8 @@
   authorized_users: [
     "google.com",
   ],
-  cl_comment_template: "Gold has detected about %d untriaged digest(s) on patchset %d. \
-View them at %s/cl/%s/%s",
+  cl_comment_template: "Gold has detected about {{.NumUntriaged}} untriaged digest(s) on patchset {{.PatchSetOrder}}.\n\
+View them at {{.InstanceURL}}/cl/{{.CRS}}/{{.ChangeListID}}",
   client_secret_file: "/etc/skia.org/login.json",
   crs_url_template: "https://pdfium-review.googlesource.com/%s",
   diff_server_grpc: "gold-pdfium-diffserver:8000",
diff --git a/golden/k8s-instances/skia-infra/skia-infra-skiacorrectness.json5 b/golden/k8s-instances/skia-infra/skia-infra-skiacorrectness.json5
index e6b4dad..09d1175 100644
--- a/golden/k8s-instances/skia-infra/skia-infra-skiacorrectness.json5
+++ b/golden/k8s-instances/skia-infra/skia-infra-skiacorrectness.json5
@@ -2,8 +2,8 @@
   authorized_users: [
     "google.com", "lovisolo-desktop@skia-public.iam.gserviceaccount.com",
   ],
-  cl_comment_template: "Gold has detected about %d untriaged digest(s) on patchset %d. \
-View them at %s/cl/%s/%s",
+  cl_comment_template: "Gold has detected about {{.NumUntriaged}} untriaged digest(s) on patchset {{.PatchSetOrder}}.\n\
+View them at {{.InstanceURL}}/cl/{{.CRS}}/{{.ChangeListID}}",
   client_secret_file: "/etc/skia.org/login.json",
   crs_url_template: "https://skia-review.googlesource.com/%s",
   diff_server_grpc: "gold-skia-infra-diffserver:8000",
diff --git a/golden/k8s-instances/skia/skia-skiacorrectness.json5 b/golden/k8s-instances/skia/skia-skiacorrectness.json5
index a9e6b93..e58be6e 100644
--- a/golden/k8s-instances/skia/skia-skiacorrectness.json5
+++ b/golden/k8s-instances/skia/skia-skiacorrectness.json5
@@ -2,8 +2,8 @@
   authorized_users: [
     "google.com", "mtklein@chromium.org", "skia-external-gm-uploader@skia-swarming-bots.iam.gserviceaccount.com",
   ],
-  cl_comment_template: "Gold has detected about %d untriaged digest(s) on patchset %d. \
-View them at %s/cl/%s/%s",
+  cl_comment_template: "Gold has detected about {{.NumUntriaged}} untriaged digest(s) on patchset {{.PatchSetOrder}}.\n\
+View them at {{.InstanceURL}}/cl/{{.CRS}}/{{.ChangeListID}}",
   client_secret_file: "/etc/skia.org/login.json",
   crs_url_template: "https://skia-review.googlesource.com/%s",
   diff_server_grpc: "gold-skia-diffserver:8000",