blob: 4cfb52cb2f0b0f9c83c5ea2f4ffb506380fa10c5 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package main
import (
var cmdLocalBuild = &subcommands.Command{
UsageLine: "localbuild <target-manifest-path> [...]",
ShortDesc: "builds a docker image using local docker daemon",
LongDesc: `Builds a docker image using local docker daemon.
Roughly does "docker build --no-cache --squash --tag <name> <context>", where
<name> and <context> come from the manifest.
Doesn't upload the image anywhere.
CommandRun: func() subcommands.CommandRun {
c := &cmdLocalBuildRun{}
return c
type cmdLocalBuildRun struct {
targetManifest string
func (c *cmdLocalBuildRun) init() {
c.commandBase.init(c.exec, extraFlags{labels: true}, []*string{
func (c *cmdLocalBuildRun) exec(ctx context.Context) error {
m, _, err := c.loadManifest(c.targetManifest, false, false)
if err != nil {
return err
labels := docker.Labels{
Created: clock.Now(ctx).UTC(),
BuildTool: UserAgent,
BuildMode: "local",
Extra: c.labels,
return stage(ctx, m, func(out *fileset.Set) error {
logging.Infof(ctx, "Sending tarball with %d files to the docker...", out.Len())
r, w := io.Pipe()
var ctxDigest string // sha256 of the context.tar.gz, FYI
var tarErr error // error when writing the tarball
// Feed the tarball to the docker daemon.
done := make(chan struct{})
go func() {
defer close(done)
ctxDigest, tarErr = sendAsTarball(out, w)
imageDigest, dockerErr := docker.Build(ctx, r, append([]string{
"--tag", m.Name, // attach a local tag for convenience
}, labels.AsBuildArgs()...))
switch {
case dockerErr != nil:
return errors.Annotate(dockerErr, "failed to build the image").Err()
case tarErr != nil:
return errors.Annotate(tarErr, "building the image").Err()
// TODO(vadimsh): Add -json-output support.
logging.Infof(ctx, "Context: %s", ctxDigest)
logging.Infof(ctx, "Image ID: %s", imageDigest)
return nil
func sendAsTarball(out *fileset.Set, w io.WriteCloser) (digest string, err error) {
h := sha256.New()
if err := out.ToTarGz(io.MultiWriter(w, h)); err != nil {
return "", errors.Annotate(err, "failed to write the tarball").Err()
if err := w.Close(); err != nil {
return "", errors.Annotate(err, "failed to close the write end of the pipe").Err()
return hex.EncodeToString(h.Sum(nil)), nil