[BCID] Fixes for build-image

With these changes, update-references works for me locally.

Change-Id: I35dfd8364e80a01714e91a9d72924e14cc9e1be6
Reviewed-on: https://skia-review.googlesource.com/c/buildbot/+/584158
Commit-Queue: Eric Boren <borenet@google.com>
Reviewed-by: Ravi Mistry <rmistry@google.com>
diff --git a/cd/go/build-images/build.go b/cd/go/build-images/build.go
index a6c5e35..d429f99 100644
--- a/cd/go/build-images/build.go
+++ b/cd/go/build-images/build.go
@@ -51,14 +51,14 @@
 
 	// Perform the builds concurrently.
 	imageInfo := &buildImagesJSON{
-		Images: make([]SingleImageInfo, 0, len(bazelTargetToImagePath)),
+		Images: make([]*SingleImageInfo, 0, len(bazelTargetToImagePath)),
 	}
 	eg, ctx := errgroup.WithContext(ctx)
 	for bazelTarget, imagePath := range bazelTargetToImagePath {
 		// https://golang.org/doc/faq#closures_and_goroutines
 		bazelTarget := bazelTarget
 		louhiImageTag := fmt.Sprintf("louhi_ws/%s:%s", imagePath, imageTag)
-		imageInfo.Images = append(imageInfo.Images, SingleImageInfo{
+		imageInfo.Images = append(imageInfo.Images, &SingleImageInfo{
 			Image: imagePath,
 			Tag:   imageTag,
 		})
diff --git a/cd/go/build-images/main.go b/cd/go/build-images/main.go
index 55d205b..f6e249c 100644
--- a/cd/go/build-images/main.go
+++ b/cd/go/build-images/main.go
@@ -144,7 +144,7 @@
 
 // buildImagesJSON describes the structure of buildImagesJSONFile.
 type buildImagesJSON struct {
-	Images []SingleImageInfo `json:"images"`
+	Images []*SingleImageInfo `json:"images"`
 }
 
 type SingleImageInfo struct {
diff --git a/cd/go/build-images/update-refs.go b/cd/go/build-images/update-refs.go
index 75cd4a9..484db89 100644
--- a/cd/go/build-images/update-refs.go
+++ b/cd/go/build-images/update-refs.go
@@ -3,6 +3,10 @@
 import (
 	"context"
 	"fmt"
+	"io/fs"
+	"io/ioutil"
+	"path/filepath"
+	"regexp"
 	"strings"
 
 	"go.skia.org/infra/go/exec"
@@ -46,7 +50,7 @@
 		if len(split) != 2 {
 			return td.FailStep(ctx, skerr.Fmt("Failed to obtain sha256 sum for %s; expected <image>@<sha256> but got %q", image.Image, output))
 		}
-		image.Sha256 = split[1]
+		image.Sha256 = strings.TrimSuffix(strings.TrimPrefix(split[1], "sha256:"), "'")
 	}
 
 	// Create a shallow clone of the repo.
@@ -65,10 +69,44 @@
 	}
 
 	// Find-and-replace each of the image references.
-	for _, image := range imageInfo.Images {
-		if _, err := exec.RunCwd(ctx, checkoutDir, "find", "./", "-type", "f", "-exec", "sed", "-r", "-i", fmt.Sprintf("s;%s@sha256:[a-f0-9]+;%s@sha256:%s;g", image.Image, image.Image, image.Sha256), "{}", "\\;"); err != nil {
-			return td.FailStep(ctx, err)
+	if err := td.Do(ctx, td.Props("Update Image References"), func(ctx context.Context) error {
+		imageRegexes := make([]*regexp.Regexp, 0, len(imageInfo.Images))
+		imageReplace := make([]string, 0, len(imageInfo.Images))
+		for _, image := range imageInfo.Images {
+			imageRegexes = append(imageRegexes, regexp.MustCompile(fmt.Sprintf(`%s@sha256:[a-f0-9]+`, image.Image)))
+			imageReplace = append(imageReplace, fmt.Sprintf("%s@sha256:%s", image.Image, image.Sha256))
 		}
+		return filepath.WalkDir(checkoutDir, func(path string, d fs.DirEntry, err error) error {
+			if err != nil {
+				return err
+			} else if d.IsDir() {
+				if d.Name() == ".git" {
+					return fs.SkipDir
+				} else {
+					return nil
+				}
+			}
+			// Read the file.
+			contents, err := ioutil.ReadFile(path)
+			if err != nil {
+				return err
+			}
+			contentsStr := string(contents)
+
+			// Replace all instances of the old image specification with the new.
+			for idx, re := range imageRegexes {
+				contentsStr = re.ReplaceAllString(contentsStr, imageReplace[idx])
+			}
+
+			// Write out the updated file.
+			contents = []byte(contentsStr)
+			if err := ioutil.WriteFile(path, contents, d.Type().Perm()); err != nil {
+				return err
+			}
+			return nil
+		})
+	}); err != nil {
+		return td.FailStep(ctx, err)
 	}
 
 	// Did we change anything?