|  | package main | 
|  |  | 
|  | import ( | 
|  | "context" | 
|  | "fmt" | 
|  | "path" | 
|  | "strings" | 
|  |  | 
|  | "go.skia.org/infra/go/exec" | 
|  | "go.skia.org/infra/go/gitauth" | 
|  | "go.skia.org/infra/go/now" | 
|  | "go.skia.org/infra/go/skerr" | 
|  | "go.skia.org/infra/task_driver/go/lib/git_steps" | 
|  | "go.skia.org/infra/task_driver/go/td" | 
|  | ) | 
|  |  | 
|  | type containerToBuild struct { | 
|  | bazelTarget string | 
|  | imageURI    string | 
|  | } | 
|  |  | 
|  | func build(ctx context.Context, commit, repo, workspace, username, email string, targets []string, extraArgs []string) error { | 
|  | ctx = td.StartStep(ctx, td.Props("Build Images")) | 
|  | defer td.EndStep(ctx) | 
|  |  | 
|  | // Initialize git authentication. | 
|  | ts, err := git_steps.Init(ctx, true) | 
|  | if err != nil { | 
|  | return td.FailStep(ctx, err) | 
|  | } | 
|  | if _, err := gitauth.New(ctx, ts, "/tmp/.gitcookies", true, email); err != nil { | 
|  | return td.FailStep(ctx, err) | 
|  | } | 
|  |  | 
|  | toBuild := make([]containerToBuild, 0, len(targets)) | 
|  | nextTarget: | 
|  | for _, target := range targets { | 
|  | targetSplit := strings.Split(target, ":") | 
|  | if len(targetSplit) != 3 { | 
|  | return td.FailStep(ctx, skerr.Fmt("Invalid target specification %q; expected \"//bazel-target:bazel-target:gcr.io/image/path\"", target)) | 
|  | } | 
|  | bazelTarget := strings.Join(targetSplit[:2], ":") | 
|  | imagePath := targetSplit[2] | 
|  |  | 
|  | c := containerToBuild{bazelTarget: bazelTarget, imageURI: imagePath} | 
|  | // Make sure we don't build the same target twice | 
|  | for _, x := range toBuild { | 
|  | if c == x { | 
|  | continue nextTarget | 
|  | } | 
|  | } | 
|  | toBuild = append(toBuild, c) | 
|  | } | 
|  |  | 
|  | // Create a shallow clone of the repo. | 
|  | checkoutDir, err := shallowClone(ctx, repo, commit) | 
|  | if err != nil { | 
|  | return td.FailStep(ctx, err) | 
|  | } | 
|  |  | 
|  | // Create the timestamped Docker image tag. | 
|  | timestamp := now.Now(ctx).UTC().Format("2006-01-02T15_04_05Z") | 
|  | imageTag := fmt.Sprintf("%s-%s-%s-%s", timestamp, username, commit[:7], "clean") | 
|  |  | 
|  | // Perform the builds. | 
|  | imageInfo := &buildImagesJSON{ | 
|  | Images: make([]*SingleImageInfo, 0, len(toBuild)), | 
|  | } | 
|  | for _, c := range toBuild { | 
|  | bazelTarget, imagePath := c.bazelTarget, c.imageURI | 
|  | tags := []string{ | 
|  | fmt.Sprintf("louhi_ws/%s:%s", imagePath, imageTag), | 
|  | fmt.Sprintf("louhi_ws/%s:git-%s", imagePath, commit), | 
|  | fmt.Sprintf("louhi_ws/%s:latest", imagePath), | 
|  | } | 
|  | imageInfo.Images = append(imageInfo.Images, &SingleImageInfo{ | 
|  | Image: imagePath, | 
|  | Tag:   imageTag, | 
|  | }) | 
|  | if err := bazelRun(ctx, checkoutDir, bazelTarget, tags, extraArgs); err != nil { | 
|  | return td.FailStep(ctx, err) | 
|  | } | 
|  | } | 
|  | return writeBuildImagesJSON(ctx, workspace, imageInfo) | 
|  | } | 
|  |  | 
|  | // bazelTargetToDockerTag converts a Bazel target specification to a Docker | 
|  | // image tag which is applied to the image during the Bazel build. | 
|  | func bazelTargetToDockerTag(target string) string { | 
|  | return path.Join("bazel", target) | 
|  | } | 
|  |  | 
|  | // bazelRun executes `bazel run` for the given target and applies the given tag | 
|  | // to the resulting image. | 
|  | func bazelRun(ctx context.Context, cwd, target string, tags []string, extraArgs []string) error { | 
|  | ctx = td.StartStep(ctx, td.Props(fmt.Sprintf("Build %s", target))) | 
|  | defer td.EndStep(ctx) | 
|  |  | 
|  | cmd := []string{"bazelisk", "run"} | 
|  | if extraArgs != nil { | 
|  | cmd = append(cmd, extraArgs...) | 
|  | } | 
|  | cmd = append(cmd, target) | 
|  | if _, err := exec.RunCwd(ctx, cwd, cmd...); err != nil { | 
|  | return td.FailStep(ctx, err) | 
|  | } | 
|  | srcTag := bazelTargetToDockerTag(target) | 
|  | for _, tag := range tags { | 
|  | if _, err := exec.RunCwd(ctx, cwd, "docker", "tag", srcTag, tag); err != nil { | 
|  | return td.FailStep(ctx, err) | 
|  | } | 
|  | } | 
|  | return nil | 
|  | } |