Skip to content
This repository has been archived by the owner on Jun 13, 2021. It is now read-only.

Commit

Permalink
Implement multi tag for build
Browse files Browse the repository at this point in the history
Signed-off-by: Ulysses Souza <ulysses.souza@docker.com>
  • Loading branch information
ulyssessouza committed Nov 29, 2019
1 parent 9169a3c commit 4c5ee74
Showing 1 changed file with 64 additions and 19 deletions.
83 changes: 64 additions & 19 deletions internal/commands/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import (
"context"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
"sync"

"github.com/deislabs/cnab-go/bundle"
cnab "github.com/deislabs/cnab-go/driver"
Expand All @@ -24,6 +26,7 @@ import (
"github.com/docker/cli/cli/command"
compose "github.com/docker/cli/cli/compose/types"
"github.com/docker/cli/cli/streams"
cliOpts "github.com/docker/cli/opts"
"github.com/docker/cnab-to-oci/remotes"
"github.com/docker/distribution/reference"
"github.com/moby/buildkit/client"
Expand All @@ -39,7 +42,7 @@ type buildOptions struct {
noCache bool
progress string
pull bool
tag string
tags cliOpts.ListOpts
folder string
imageIDFile string
args []string
Expand All @@ -48,10 +51,12 @@ type buildOptions struct {
}

const buildExample = `- $ docker app build .
- $ docker app build --file myapp.dockerapp --tag myrepo/myapp:1.0.0 .`
- $ docker app build --file myapp.dockerapp --tag myrepo/myapp:1.0.0 --tag myrepo/myapp:latest .`

func Cmd(dockerCli command.Cli) *cobra.Command {
var opts buildOptions
opts := buildOptions{
tags: cliOpts.NewListOpts(validateTag),
}
cmd := &cobra.Command{
Use: "build [OPTIONS] BUILD_PATH",
Short: "Build an App image from an App definition (.dockerapp)",
Expand All @@ -66,7 +71,7 @@ func Cmd(dockerCli command.Cli) *cobra.Command {
flags.BoolVar(&opts.noCache, "no-cache", false, "Do not use cache when building the App image")
flags.StringVar(&opts.progress, "progress", "auto", "Set type of progress output (auto, plain, tty). Use plain to show container output")
flags.BoolVar(&opts.noResolveImage, "no-resolve-image", false, "Do not query the registry to resolve image digest")
flags.StringVarP(&opts.tag, "tag", "t", "", "App image tag, optionally in the 'repo:tag' format")
flags.VarP(&opts.tags, "tag", "t", "Name and optionally a tag in the 'name:tag' format")
flags.StringVarP(&opts.folder, "file", "f", "", "App definition as a .dockerapp directory")
flags.BoolVar(&opts.pull, "pull", false, "Always attempt to pull a newer version of the App image")
flags.StringArrayVar(&opts.args, "build-arg", []string{}, "Set build-time variables")
Expand Down Expand Up @@ -128,37 +133,66 @@ func runBuild(dockerCli command.Cli, contextPath string, opt buildOptions) error
}
defer app.Cleanup()

bundle, err := buildImageUsingBuildx(app, contextPath, opt, dockerCli)
bndl, err := buildImageUsingBuildx(app, contextPath, opt, dockerCli)
if err != nil {
return err
}

var ref reference.Reference
ref, err = packager.GetNamedTagged(opt.tag)
out, err := getOutputFile(dockerCli.Out(), opt.quiet)
if err != nil {
return err
}

id, err := packager.PersistInBundleStore(ref, bundle)
id, err := persistTags(bndl, opt.tags, opt.imageIDFile, out, dockerCli.Err())
if err != nil {
return err
}

if opt.imageIDFile != "" {
if err = ioutil.WriteFile(opt.imageIDFile, []byte(id.Digest().String()), 0644); err != nil {
fmt.Fprintf(dockerCli.Err(), "Failed to write App image ID in %s: %s", opt.imageIDFile, err)
}
if opt.quiet {
_, err = fmt.Fprintln(dockerCli.Out(), id.Digest().String())
}
return err
}

if opt.quiet {
fmt.Fprintln(dockerCli.Out(), id.Digest().String())
return err
func persistTags(bndl *bundle.Bundle, tags cliOpts.ListOpts, iidFile string, outWriter io.Writer, errWriter io.Writer) (reference.Digested, error) {
var id reference.Digested
var onceWriteIIDFile sync.Once
if tags.Len() == 0 {
err := tags.Set("")
if err != nil {
return nil, err
}
}
fmt.Fprintf(dockerCli.Out(), "Successfully built %s\n", id.String())
if ref != nil {
fmt.Fprintf(dockerCli.Out(), "Successfully tagged %s\n", ref.String())
for _, tag := range tags.GetAll() {
ref, err := packager.GetNamedTagged(tag)
if err != nil {
return nil, err
}
id, err = persistInBundleStore(&onceWriteIIDFile, outWriter, errWriter, bndl, ref, iidFile)
if err != nil {
return nil, err
}
if tag != "" {
fmt.Fprintf(outWriter, "Successfully tagged app %s\n", ref.String())
}
}
return err
return id, nil
}

func persistInBundleStore(once *sync.Once, out io.Writer, errWriter io.Writer, b *bundle.Bundle, ref reference.Reference, iidFileName string) (reference.Digested, error) {
id, err := packager.PersistInBundleStore(ref, b)
if err != nil {
return nil, err
}
once.Do(func() {
fmt.Fprintf(out, "Successfully built app %s\n", id.String())
if iidFileName != "" {
if err := ioutil.WriteFile(iidFileName, []byte(id.Digest().String()), 0644); err != nil {
fmt.Fprintf(errWriter, "Failed to write App image ID in %s: %s", iidFileName, err)
}
}
})
return id, nil
}

func buildImageUsingBuildx(app *types.App, contextPath string, opt buildOptions, dockerCli command.Cli) (*bundle.Bundle, error) {
Expand Down Expand Up @@ -361,3 +395,14 @@ func checkBuildArgsUniqueness(args []string) error {
}
return nil
}

// validateTag checks if the given image name can be resolved.
func validateTag(rawRepo string) (string, error) {
if rawRepo != "" {
_, err := reference.ParseNormalizedNamed(rawRepo)
if err != nil {
return "", err
}
}
return rawRepo, nil
}

0 comments on commit 4c5ee74

Please sign in to comment.