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

Commit

Permalink
Merge pull request #691 from ndeloof/build_more
Browse files Browse the repository at this point in the history
Adjust the "docker app build" UX
  • Loading branch information
silvin-lubecki authored Oct 17, 2019
2 parents 175bec6 + 52b815d commit c622dcf
Show file tree
Hide file tree
Showing 8 changed files with 61 additions and 36 deletions.
4 changes: 2 additions & 2 deletions e2e/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func TestBuild(t *testing.T) {
cmd := info.configuredCmd

testDir := path.Join("testdata", "build")
cmd.Command = dockerCli.Command("app", "build", path.Join(testDir, "single"), "--tag", "single:1.0.0")
cmd.Command = dockerCli.Command("app", "build", "--tag", "single:1.0.0", "-f", path.Join(testDir, "single.dockerapp"), testDir)
icmd.RunCmd(cmd).Assert(t, icmd.Success)

cfg := getDockerConfigDir(t, cmd)
Expand All @@ -42,7 +42,7 @@ func TestBuildWithoutTag(t *testing.T) {
cmd := info.configuredCmd

testDir := path.Join("testdata", "build")
cmd.Command = dockerCli.Command("app", "build", path.Join(testDir, "single"))
cmd.Command = dockerCli.Command("app", "build", "-f", path.Join(testDir, "single.dockerapp"), testDir)
icmd.RunCmd(cmd).Assert(t, icmd.Success)

cfg := getDockerConfigDir(t, cmd)
Expand Down
8 changes: 5 additions & 3 deletions e2e/commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,9 @@ func TestRenderFormatters(t *testing.T) {
runWithDindSwarmAndRegistry(t, func(info dindSwarmAndRegistryInfo) {
cmd := info.configuredCmd

appPath := filepath.Join("testdata", "simple", "simple.dockerapp")
cmd.Command = dockerCli.Command("app", "build", appPath, "--tag", "a-simple-tag")
contextPath := filepath.Join("testdata", "simple")
appPath := filepath.Join(contextPath, "simple.dockerapp")
cmd.Command = dockerCli.Command("app", "build", "--tag", "a-simple-tag", contextPath)
icmd.RunCmd(cmd).Assert(t, icmd.Success)

cmd.Command = dockerCli.Command("app", "render", "--formatter", "json", appPath)
Expand Down Expand Up @@ -164,7 +165,8 @@ func TestInspectApp(t *testing.T) {
Err: `"docker app inspect" requires exactly 1 argument.`,
})

cmd.Command = dockerCli.Command("app", "build", filepath.Join("testdata", "simple", "simple.dockerapp"), "--tag", "simple-app:1.0.0")
contextPath := filepath.Join("testdata", "simple")
cmd.Command = dockerCli.Command("app", "build", "--tag", "simple-app:1.0.0", contextPath)
cmd.Dir = ""
icmd.RunCmd(cmd).Assert(t, icmd.Success)

Expand Down
10 changes: 5 additions & 5 deletions e2e/images_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ import (

func insertBundles(t *testing.T, cmd icmd.Cmd, info dindSwarmAndRegistryInfo) {
// Push an application so that we can later pull it by digest
cmd.Command = dockerCli.Command("app", "build", filepath.Join("testdata", "push-pull", "push-pull.dockerapp"), "--tag", info.registryAddress+"/c-myapp")
cmd.Command = dockerCli.Command("app", "build", "--tag", info.registryAddress+"/c-myapp", filepath.Join("testdata", "push-pull"))
icmd.RunCmd(cmd).Assert(t, icmd.Success)
cmd.Command = dockerCli.Command("app", "build", filepath.Join("testdata", "simple", "simple.dockerapp"), "--tag", "b-simple-app")
cmd.Command = dockerCli.Command("app", "build", "--tag", "b-simple-app", filepath.Join("testdata", "simple"))
icmd.RunCmd(cmd).Assert(t, icmd.Success)
cmd.Command = dockerCli.Command("app", "build", filepath.Join("testdata", "simple", "simple.dockerapp"), "--tag", "a-simple-app")
cmd.Command = dockerCli.Command("app", "build", "--tag", "a-simple-app", filepath.Join("testdata", "simple"))
icmd.RunCmd(cmd).Assert(t, icmd.Success)
}

Expand Down Expand Up @@ -86,7 +86,7 @@ func TestImageTag(t *testing.T) {
}

// given a first available image
cmd.Command = dockerCli.Command("app", "build", filepath.Join("testdata", "simple", "simple.dockerapp"), "--tag", "a-simple-app")
cmd.Command = dockerCli.Command("app", "build", "--tag", "a-simple-app", filepath.Join("testdata", "simple"))
icmd.RunCmd(cmd).Assert(t, icmd.Success)

singleImageExpectation := `APP IMAGE APP NAME
Expand Down Expand Up @@ -175,7 +175,7 @@ c-simple-app:latest simple
`)

// given a new application
cmd.Command = dockerCli.Command("app", "build", filepath.Join("testdata", "push-pull", "push-pull.dockerapp"), "--tag", "push-pull")
cmd.Command = dockerCli.Command("app", "build", "--tag", "push-pull", filepath.Join("testdata", "push-pull"))
icmd.RunCmd(cmd).Assert(t, icmd.Success)
expectImageListOutput(t, cmd, `APP IMAGE APP NAME
a-simple-app:0.1 simple
Expand Down
4 changes: 2 additions & 2 deletions e2e/pushpull_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ func TestPushInstallBundle(t *testing.T) {
ref := info.registryAddress + "/test/push-bundle"

// render the app to a bundle, we use the app from the push pull test above.
cmd.Command = dockerCli.Command("app", "build", filepath.Join("testdata", "push-pull", "push-pull.dockerapp"), "--tag", "a-simple-app:1.0.0")
cmd.Command = dockerCli.Command("app", "build", "--tag", "a-simple-app:1.0.0", filepath.Join("testdata", "push-pull"))
icmd.RunCmd(cmd).Assert(t, icmd.Success)

// push it and install to check it is available
Expand Down Expand Up @@ -244,7 +244,7 @@ func TestPushInstallBundle(t *testing.T) {
cmdIsolatedStore.Env = append(cmdIsolatedStore.Env, "DOCKER_CONTEXT=swarm-context")

// bundle the app again but this time with a tag to store it into the bundle store
cmdIsolatedStore.Command = dockerCli.Command("app", "build", filepath.Join("testdata", "push-pull", "push-pull.dockerapp"), "--tag", ref2)
cmdIsolatedStore.Command = dockerCli.Command("app", "build", "--tag", ref2, filepath.Join("testdata", "push-pull"))
icmd.RunCmd(cmdIsolatedStore).Assert(t, icmd.Success)
// Push the app without tagging it explicitly
cmdIsolatedStore.Command = dockerCli.Command("app", "push", ref2)
Expand Down
39 changes: 35 additions & 4 deletions internal/commands/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"

Expand Down Expand Up @@ -34,14 +36,15 @@ type buildOptions struct {
progress string
pull bool
tag string
folder string
}

func Cmd(dockerCli command.Cli) *cobra.Command {
var opts buildOptions
cmd := &cobra.Command{
Use: "build [APP_NAME] [OPTIONS]",
Use: "build [OPTIONS] [CONTEXT_PATH]",
Short: "Build service images for the application",
Example: `$ docker app build myapp.dockerapp --tag my/app:1.0.0`,
Example: `$ docker app build --tag my/app:1.0.0 .`,
Args: cli.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
ref, err := runBuild(dockerCli, args[0], opts)
Expand All @@ -56,12 +59,13 @@ func Cmd(dockerCli command.Cli) *cobra.Command {
flags.BoolVar(&opts.noCache, "no-cache", false, "Do not use cache when building the image")
flags.StringVar(&opts.progress, "progress", "auto", "Set type of progress output (auto, plain, tty). Use plain to show container output")
flags.StringVarP(&opts.tag, "tag", "t", "", "Application image and optionally a tag in the 'image:tag' format")
flags.StringVarP(&opts.folder, "folder", "f", "", "Docker app folder containing application definition")
flags.BoolVar(&opts.pull, "pull", false, "Always attempt to pull a newer version of the image")

return cmd
}

func runBuild(dockerCli command.Cli, application string, opt buildOptions) (reference.Reference, error) {
func runBuild(dockerCli command.Cli, contextPath string, opt buildOptions) (reference.Reference, error) {
err := checkMinimalEngineVersion(dockerCli)
if err != nil {
return nil, err
Expand All @@ -73,13 +77,18 @@ func runBuild(dockerCli command.Cli, application string, opt buildOptions) (refe
return nil, err
}

application, err := getAppFolder(opt, contextPath)
if err != nil {
return nil, err
}

app, err := packager.Extract(application)
if err != nil {
return nil, err
}
defer app.Cleanup()

buildopts, err := parseCompose(app, opt)
buildopts, err := parseCompose(app, contextPath, opt)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -126,6 +135,28 @@ func runBuild(dockerCli command.Cli, application string, opt buildOptions) (refe
return packager.PersistInBundleStore(ref, bundle)
}

func getAppFolder(opt buildOptions, contextPath string) (string, error) {
application := opt.folder
if application == "" {
files, err := ioutil.ReadDir(contextPath)
if err != nil {
return "", err
}
for _, f := range files {
if strings.HasSuffix(f.Name(), ".dockerapp") {
if application != "" {
return "", fmt.Errorf("%s contains multiple *.dockerapp folders, use -f option to select the one to build", contextPath)
}
application = filepath.Join(contextPath, f.Name())
if !f.IsDir() {
return "", fmt.Errorf("%s isn't a directory", f.Name())
}
}
}
}
return application, nil
}

func checkMinimalEngineVersion(dockerCli command.Cli) error {
info, err := dockerCli.Client().Info(appcontext.Context())
if err != nil {
Expand Down
15 changes: 5 additions & 10 deletions internal/commands/build/compose.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (

// parseCompose do parse app compose file and extract buildx Options
// We don't rely on bake's ReadTargets + TargetsToBuildOpt here as we have to skip environment variable interpolation
func parseCompose(app *types.App, options buildOptions) (map[string]build.Options, error) {
func parseCompose(app *types.App, contextPath string, options buildOptions) (map[string]build.Options, error) {
parsed, err := loader.ParseYAML(app.Composes()[0])
if err != nil {
return nil, err
Expand All @@ -28,23 +28,18 @@ func parseCompose(app *types.App, options buildOptions) (map[string]build.Option
if service.Build == nil {
continue
}

var tags []string
if service.Image != nil && *service.Image != "" {
tags = []string{*service.Image}
if service.Image != nil {
tags = append(tags, *service.Image)
}

// FIXME docker app init should update relative paths
// compose file has been copied to x.dockerapp, so the relative path to build context get broken
contextPath := path.Join(app.Path, "..", service.Build.Context)
if service.Build.Dockerfile == "" {
service.Build.Dockerfile = "Dockerfile"
}
dockerfile := path.Join(contextPath, service.Build.Dockerfile)
opts[service.Name] = build.Options{
Inputs: build.Inputs{
ContextPath: contextPath,
DockerfilePath: dockerfile,
ContextPath: path.Join(contextPath, service.Build.Context),
DockerfilePath: path.Join(contextPath, service.Build.Context, service.Build.Dockerfile),
},
BuildArgs: flatten(service.Build.Args),
NoCache: options.noCache,
Expand Down
4 changes: 1 addition & 3 deletions internal/commands/build/compose_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,9 @@ func Test_parseCompose(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

app, err := packager.Extract("testdata/" + tt.name)
assert.NilError(t, err)

got, err := parseCompose(app, buildOptions{})
got, err := parseCompose(app, "testdata", buildOptions{})
assert.NilError(t, err)
_, ok := got["dontwant"]
assert.Assert(t, !ok, "parseCompose() should have excluded 'dontwant' service")
Expand Down
13 changes: 6 additions & 7 deletions internal/packager/extract.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,7 @@ import (
)

// findApp looks for an app in CWD or subdirs
func findApp() (string, error) {
cwd, err := os.Getwd()
if err != nil {
return "", errors.Wrap(err, "cannot resolve current working directory")
}
func findApp(cwd string) (string, error) {
if strings.HasSuffix(cwd, internal.AppExtension) {
return cwd, nil
}
Expand Down Expand Up @@ -47,8 +43,11 @@ func findApp() (string, error) {
// If nothing is found, it looks for an image and loads it
func Extract(name string, ops ...func(*types.App) error) (*types.App, error) {
if name == "" {
var err error
if name, err = findApp(); err != nil {
cwd, err := os.Getwd()
if err != nil {
return nil, errors.Wrap(err, "cannot resolve current working directory")
}
if name, err = findApp(cwd); err != nil {
return nil, err
}
}
Expand Down

0 comments on commit c622dcf

Please sign in to comment.