From bb599ae3430712136ad292db5c70092c9fee9278 Mon Sep 17 00:00:00 2001 From: davidmdm Date: Fri, 28 May 2021 09:36:48 -0400 Subject: [PATCH 1/5] more robust container names --- src/cmd/tidy/tidy.go | 8 +--- src/internal/context_test.go | 12 ++--- src/internal/yey.go | 43 +++++++++++++++-- src/internal/yey_test.go | 89 ++++++++++++++++++++++++++++++++++++ 4 files changed, 134 insertions(+), 18 deletions(-) create mode 100644 src/internal/yey_test.go diff --git a/src/cmd/tidy/tidy.go b/src/cmd/tidy/tidy.go index 159b4c2..b7bea9b 100644 --- a/src/cmd/tidy/tidy.go +++ b/src/cmd/tidy/tidy.go @@ -2,8 +2,6 @@ package tidy import ( "context" - "fmt" - "path/filepath" "strings" "github.com/spf13/cobra" @@ -39,11 +37,7 @@ func run(ctx context.Context) error { validNames[yey.ContainerName(contexts.Path, ctx)] = struct{}{} } - prefix := fmt.Sprintf( - "yey-%s-%s", - filepath.Base(filepath.Dir(contexts.Path)), - yey.Hash(contexts.Path), - ) + prefix := yey.ContainerPathPrefix(contexts.Path) names, err := docker.ListContainers(ctx) if err != nil { diff --git a/src/internal/context_test.go b/src/internal/context_test.go index 09d20ac..f683461 100644 --- a/src/internal/context_test.go +++ b/src/internal/context_test.go @@ -84,32 +84,32 @@ func TestMerge(t *testing.T) { func TestSameHashesForSameContexts(t *testing.T) { ctx1 := getCtx1() - hash1 := Hash(ctx1.String()) + hash1 := hash(ctx1.String()) ctx2 := getCtx1() - hash2 := Hash(ctx2.String()) + hash2 := hash(ctx2.String()) assert.Equal(t, hash1, hash2) } func TestDifferentHashesForDifferentEnvs(t *testing.T) { ctx1 := getCtx1() - hash1 := Hash(ctx1.String()) + hash1 := hash(ctx1.String()) ctx2 := getCtx1() ctx2.Env["ENV1"] = "value1b" - hash2 := Hash(ctx2.String()) + hash2 := hash(ctx2.String()) assert.NotEqual(t, hash1, hash2) } func TestDifferentHashesForDifferentMounts(t *testing.T) { ctx1 := getCtx1() - hash1 := Hash(ctx1.String()) + hash1 := hash(ctx1.String()) ctx2 := getCtx1() ctx2.Mounts["/local/mount1"] = "/container/mount1b" - hash2 := Hash(ctx2.String()) + hash2 := hash(ctx2.String()) assert.NotEqual(t, hash1, hash2) } diff --git a/src/internal/yey.go b/src/internal/yey.go index 1c80084..e502c2f 100644 --- a/src/internal/yey.go +++ b/src/internal/yey.go @@ -6,9 +6,18 @@ import ( "hash/crc64" "io" "path/filepath" + "regexp" ) -func Hash(value string) string { +var ( + spaces = regexp.MustCompile(`\s+`) + special = regexp.MustCompile(`[^a-zA-Z0-9.\-_]`) + dashes = regexp.MustCompile(`-+`) + alphaNumericStart = regexp.MustCompile(`^[a-zA-Z0-9]`) + trailingDashes = regexp.MustCompile(`-+$`) +) + +func hash(value string) string { hasher := crc64.New(crc64.MakeTable(crc64.ECMA)) io.WriteString(hasher, value) return hex.EncodeToString(hasher.Sum(nil)) @@ -16,10 +25,34 @@ func Hash(value string) string { func ContainerName(path string, context Context) string { return fmt.Sprintf( - "yey-%s-%s-%s-%s", - filepath.Base(filepath.Dir(path)), - Hash(path), + "%s-%s-%s", + ContainerPathPrefix(path), context.Name, - Hash(context.String()), + hash(context.String()), ) } + +func sanitizePathName(value string) string { + value = spaces.ReplaceAllString(value, "_") + value = special.ReplaceAllString(value, "") + value = dashes.ReplaceAllString(value, "-") + value = trailingDashes.ReplaceAllString(value, "") + + if value == "" { + return "" + } + + if !alphaNumericStart.MatchString(value) { + value = "0" + value + } + + return value +} + +func ContainerPathPrefix(path string) string { + pathBase := sanitizePathName(filepath.Base(filepath.Dir(path))) + if pathBase == "" { + return fmt.Sprintf("yey-%s", hash(path)) + } + return fmt.Sprintf("yey-%s-%s", pathBase, hash(path)) +} diff --git a/src/internal/yey_test.go b/src/internal/yey_test.go new file mode 100644 index 0000000..3cb4471 --- /dev/null +++ b/src/internal/yey_test.go @@ -0,0 +1,89 @@ +package yey + +import "testing" + +func TestSanitizePathName(t *testing.T) { + testCases := []struct { + Name string + Value string + Expected string + }{ + { + Name: "removes space from name", + Value: "my folder with spaces", + Expected: "my_folder_with_spaces", + }, + { + Name: "strips invalid characters", + Value: "my&folder#cool*stuff!", + Expected: "myfoldercoolstuff", + }, + { + Name: "padds with 0 if does not start with alphanumeric character", + Value: "_valid_if_not_for_underscore_start", + Expected: "0_valid_if_not_for_underscore_start", + }, + { + Name: "reduces multidashes to single dash", + Value: "my---folder----path", + Expected: "my-folder-path", + }, + { + Name: "strips trailing dashes from name", + Value: "my_folder----", + Expected: "my_folder", + }, + { + Name: "empty string", + Value: "", + Expected: "", + }, + { + Name: "full strip returns empty string", + Value: "$%^&", + Expected: "", + }, + } + + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + actual := sanitizePathName(tc.Value) + if actual != tc.Expected { + t.Fatalf("expected %s but got: %s", tc.Expected, actual) + } + }) + } +} + +func TestContainerPathPrefix(t *testing.T) { + testCases := []struct { + Name string + Value string + Expected string + }{ + { + Name: "simple base name with hash prefix", + Value: "/root/projectName/.yeyrc.yaml", + Expected: "yey-projectName-45c6afaff136ad78", + }, + { + Name: "root path", + Value: "/.yeyrc.yaml", + Expected: "yey-8767bf26c174b05d", + }, + { + Name: "path base that sanitizes to empty string", + Value: "/specialBase/#%!/.yeyrc.yaml", + Expected: "yey-19bd96cb89cf189c", + }, + } + + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + actual := ContainerPathPrefix(tc.Value) + if actual != tc.Expected { + t.Fatalf("expected %s but got: %s", tc.Expected, actual) + } + }) + } +} From 1a2e033b4f9f942b200f7a4b050a10e0c81ae669 Mon Sep 17 00:00:00 2001 From: davidmdm Date: Fri, 28 May 2021 09:52:58 -0400 Subject: [PATCH 2/5] base remove command added --- src/cmd/remove/remove.go | 53 ++++++++++++++++++++++++++++++++++++++++ src/main.go | 2 ++ 2 files changed, 55 insertions(+) create mode 100644 src/cmd/remove/remove.go diff --git a/src/cmd/remove/remove.go b/src/cmd/remove/remove.go new file mode 100644 index 0000000..5272b70 --- /dev/null +++ b/src/cmd/remove/remove.go @@ -0,0 +1,53 @@ +package remove + +import ( + "context" + "fmt" + + "github.com/spf13/cobra" + + "github.com/silphid/yey/src/cmd" + yey "github.com/silphid/yey/src/internal" + "github.com/silphid/yey/src/internal/docker" +) + +// New creates a cobra command +func New() *cobra.Command { + return &cobra.Command{ + Use: "remove", + Aliases: []string{"rm"}, + Short: "Removes context container", + Args: cobra.RangeArgs(0, 1), + RunE: func(cmd *cobra.Command, args []string) error { + name := "" + if len(args) == 1 { + name = args[0] + } + return run(cmd.Context(), name) + }, + } +} + +func run(ctx context.Context, name string) error { + contexts, err := yey.LoadContexts() + if err != nil { + return err + } + + if name == "" { + var err error + name, err = cmd.PromptContext(contexts) + if err != nil { + return fmt.Errorf("failed to prompt for desired context: %w", err) + } + } + + context, err := contexts.GetContext(name) + if err != nil { + return fmt.Errorf("failed to get context with name %q: %w", name, err) + } + + container := yey.ContainerName(contexts.Path, context) + + return docker.Remove(ctx, container) +} diff --git a/src/main.go b/src/main.go index 3d8c5f6..c6a02f0 100644 --- a/src/main.go +++ b/src/main.go @@ -8,6 +8,7 @@ import ( "github.com/silphid/yey/src/cmd" "github.com/silphid/yey/src/cmd/get" + "github.com/silphid/yey/src/cmd/remove" getcontainers "github.com/silphid/yey/src/cmd/get/containers" getcontext "github.com/silphid/yey/src/cmd/get/context" @@ -33,6 +34,7 @@ func main() { rootCmd.AddCommand(run.New()) rootCmd.AddCommand(versioning.New(version)) rootCmd.AddCommand(tidy.New()) + rootCmd.AddCommand(remove.New()) getCmd := get.New() getCmd.AddCommand(getcontext.New()) From 28b7983ea92f74dcd02a502ff6c203ef32e1fb05 Mon Sep 17 00:00:00 2001 From: davidmdm Date: Fri, 28 May 2021 13:16:40 -0400 Subject: [PATCH 3/5] support dockerfiles --- examples/buildexample/.yeyrc.yaml | 3 +++ examples/buildexample/Dockerfile | 3 +++ go.mod | 2 +- go.sum | 1 + src/cmd/run/run.go | 26 +++++++++++++++++++++++ src/internal/context.go | 17 +++++++++++++++ src/internal/contextFile.go | 35 ++++++++++++++++++++++++++++--- src/internal/docker/cli.go | 14 +++++++++++++ src/internal/yey.go | 4 ++++ 9 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 examples/buildexample/.yeyrc.yaml create mode 100644 examples/buildexample/Dockerfile diff --git a/examples/buildexample/.yeyrc.yaml b/examples/buildexample/.yeyrc.yaml new file mode 100644 index 0000000..40b6bb4 --- /dev/null +++ b/examples/buildexample/.yeyrc.yaml @@ -0,0 +1,3 @@ +build: + dockerfile: ./Dockerfile + context: . diff --git a/examples/buildexample/Dockerfile b/examples/buildexample/Dockerfile new file mode 100644 index 0000000..254b321 --- /dev/null +++ b/examples/buildexample/Dockerfile @@ -0,0 +1,3 @@ +FROM ubuntu:latest + +CMD ["bash"] \ No newline at end of file diff --git a/go.mod b/go.mod index 6e8c5ff..24feb09 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/silphid/yey -go 1.14 +go 1.16 require ( github.com/AlecAivazis/survey/v2 v2.2.12 diff --git a/go.sum b/go.sum index 8b6bfe7..ca68029 100644 --- a/go.sum +++ b/go.sum @@ -178,6 +178,7 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= diff --git a/src/cmd/run/run.go b/src/cmd/run/run.go index 51cdefc..633bbff 100644 --- a/src/cmd/run/run.go +++ b/src/cmd/run/run.go @@ -3,9 +3,11 @@ package run import ( "context" "fmt" + "os" yey "github.com/silphid/yey/src/internal" "github.com/silphid/yey/src/internal/docker" + "github.com/silphid/yey/src/internal/logging" "github.com/silphid/yey/src/cmd" @@ -65,6 +67,15 @@ func run(ctx context.Context, name string, options Options) error { yeyContext.Remove = options.Remove } + if yeyContext.Image == "" { + var err error + yeyContext.Image, err = readAndBuildDockerfile(ctx, yeyContext.Build) + if err != nil { + return fmt.Errorf("failed to build yey context image: %w", err) + } + logging.Log("built image: %s", yeyContext.Image) + } + containerName := yey.ContainerName(contexts.Path, yeyContext) if options.Reset { @@ -75,3 +86,18 @@ func run(ctx context.Context, name string, options Options) error { return docker.Start(ctx, yeyContext, containerName) } + +func readAndBuildDockerfile(ctx context.Context, build yey.DockerBuild) (string, error) { + dockerBytes, err := os.ReadFile(build.Dockerfile) + if err != nil { + return "", fmt.Errorf("failed to read dockerfile: %w", err) + } + + imageName := yey.ImageName(dockerBytes) + + if err := docker.Build(ctx, build.Dockerfile, imageName, build.Args, build.Context); err != nil { + return "", fmt.Errorf("failed to build image: %w", err) + } + + return imageName, nil +} diff --git a/src/internal/context.go b/src/internal/context.go index df30cc2..9cff009 100644 --- a/src/internal/context.go +++ b/src/internal/context.go @@ -4,11 +4,18 @@ import ( "gopkg.in/yaml.v2" ) +type DockerBuild struct { + Dockerfile string + Args map[string]string + Context string +} + // Context represents execution configuration for some docker container type Context struct { Name string `yaml:",omitempty"` Remove *bool Image string + Build DockerBuild Env map[string]string Mounts map[string]string Cmd []string @@ -30,6 +37,10 @@ func (c Context) Clone() Context { for key, value := range c.Mounts { clone.Mounts[key] = value } + clone.Build.Args = make(map[string]string) + for key, value := range c.Build.Args { + clone.Build.Args[key] = value + } return clone } @@ -49,6 +60,12 @@ func (c Context) Merge(source Context) Context { for key, value := range source.Mounts { merged.Mounts[key] = value } + if source.Build.Dockerfile != "" { + merged.Build.Dockerfile = source.Build.Dockerfile + } + for key, value := range source.Build.Args { + merged.Build.Args[key] = value + } return merged } diff --git a/src/internal/contextFile.go b/src/internal/contextFile.go index 78c4f72..0432671 100644 --- a/src/internal/contextFile.go +++ b/src/internal/contextFile.go @@ -74,7 +74,7 @@ func readContextFileFromNetwork(url string) ([]byte, error) { } // parseContextFile unmarshals the contextFile data and resolves any parent contextfiles -func parseContextFile(data []byte) (Contexts, error) { +func parseContextFile(root string, data []byte) (Contexts, error) { var ctxFile ContextFile if err := yaml.Unmarshal(data, &ctxFile); err != nil { return Contexts{}, fmt.Errorf("failed to decode context file: %w", err) @@ -89,6 +89,13 @@ func parseContextFile(data []byte) (Contexts, error) { Named: ctxFile.Named, } + if root != "" { + contexts.Context = resolveContextPaths(root, contexts.Context) + for name, context := range contexts.Named { + contexts.Named[name] = resolveContextPaths(root, context) + } + } + if ctxFile.Parent != "" { parent, err := readAndParseContextFileFromURI(ctxFile.Parent) if err != nil { @@ -105,10 +112,12 @@ func parseContextFile(data []byte) (Contexts, error) { func readAndParseContextFileFromURI(path string) (Contexts, error) { var bytes []byte var err error + var root string if strings.HasPrefix(path, "https:") || strings.HasPrefix(path, "http:") { bytes, err = readContextFileFromNetwork(path) } else { + root = filepath.Dir(path) bytes, err = readContextFileFromFilePath(path) } @@ -116,7 +125,7 @@ func readAndParseContextFileFromURI(path string) (Contexts, error) { return Contexts{}, fmt.Errorf("failed to read context file: %w", err) } - return parseContextFile(bytes) + return parseContextFile(root, bytes) } // LoadContexts reads the context file and returns the contexts. It starts by reading from current @@ -127,7 +136,7 @@ func LoadContexts() (Contexts, error) { return Contexts{}, fmt.Errorf("failed to read context file: %w", err) } - contexts, err := parseContextFile(bytes) + contexts, err := parseContextFile(filepath.Dir(path), bytes) if err != nil { return Contexts{}, err } @@ -135,3 +144,23 @@ func LoadContexts() (Contexts, error) { return contexts, nil } + +func resolveContextPaths(root string, context Context) Context { + clone := context.Clone() + clone.Build.Dockerfile = resolvePath(root, context.Build.Dockerfile) + clone.Build.Context = resolvePath(root, clone.Build.Context) + for key, value := range clone.Mounts { + clone.Mounts[resolvePath(root, key)] = value + } + return clone +} + +func resolvePath(root, path string) string { + if path == "" { + return "" + } + if strings.HasPrefix(path, "/") { + return path + } + return filepath.Join(root, path) +} diff --git a/src/internal/docker/cli.go b/src/internal/docker/cli.go index b91ab07..0923175 100644 --- a/src/internal/docker/cli.go +++ b/src/internal/docker/cli.go @@ -6,6 +6,7 @@ import ( "fmt" "os" "os/exec" + "path/filepath" "regexp" "strings" @@ -45,6 +46,19 @@ func Remove(ctx context.Context, containerName string) error { return attachStdPipes(exec.CommandContext(ctx, "docker", "rm", "-v", containerName)).Run() } +func Build(ctx context.Context, dockerPath string, imageName string, buildArgs map[string]string, context string) error { + args := []string{"build", "-f", dockerPath, "-t", imageName} + for key, value := range buildArgs { + args = append(args, "--build-arg", fmt.Sprintf("%s=%q", key, value)) + } + if context == "" { + context = filepath.Dir(dockerPath) + } + args = append(args, context) + + return attachStdPipes(exec.CommandContext(ctx, "docker", args...)).Run() +} + var newlines = regexp.MustCompile(`\r?\n`) func ListContainers(ctx context.Context) ([]string, error) { diff --git a/src/internal/yey.go b/src/internal/yey.go index e502c2f..42f4da8 100644 --- a/src/internal/yey.go +++ b/src/internal/yey.go @@ -56,3 +56,7 @@ func ContainerPathPrefix(path string) string { } return fmt.Sprintf("yey-%s-%s", pathBase, hash(path)) } + +func ImageName(dockerfile []byte) string { + return fmt.Sprintf("yey-%s", hash(string(dockerfile))) +} From 0b7b4a8bfcdbf6cfc0656fe14bd0663c138a92d6 Mon Sep 17 00:00:00 2001 From: davidmdm Date: Fri, 28 May 2021 13:42:50 -0400 Subject: [PATCH 4/5] check image tag before building --- src/cmd/run/run.go | 2 +- src/internal/contextFile.go | 30 ++++++++++++++++-------------- src/internal/docker/cli.go | 22 ++++++++++++++++++++-- 3 files changed, 37 insertions(+), 17 deletions(-) diff --git a/src/cmd/run/run.go b/src/cmd/run/run.go index 633bbff..d411e9b 100644 --- a/src/cmd/run/run.go +++ b/src/cmd/run/run.go @@ -73,7 +73,7 @@ func run(ctx context.Context, name string, options Options) error { if err != nil { return fmt.Errorf("failed to build yey context image: %w", err) } - logging.Log("built image: %s", yeyContext.Image) + logging.Log("using image: %s", yeyContext.Image) } containerName := yey.ContainerName(contexts.Path, yeyContext) diff --git a/src/internal/contextFile.go b/src/internal/contextFile.go index 0432671..a400492 100644 --- a/src/internal/contextFile.go +++ b/src/internal/contextFile.go @@ -74,7 +74,7 @@ func readContextFileFromNetwork(url string) ([]byte, error) { } // parseContextFile unmarshals the contextFile data and resolves any parent contextfiles -func parseContextFile(root string, data []byte) (Contexts, error) { +func parseContextFile(dir string, data []byte) (Contexts, error) { var ctxFile ContextFile if err := yaml.Unmarshal(data, &ctxFile); err != nil { return Contexts{}, fmt.Errorf("failed to decode context file: %w", err) @@ -89,10 +89,10 @@ func parseContextFile(root string, data []byte) (Contexts, error) { Named: ctxFile.Named, } - if root != "" { - contexts.Context = resolveContextPaths(root, contexts.Context) + if dir != "" { + contexts.Context = resolveContextPaths(dir, contexts.Context) for name, context := range contexts.Named { - contexts.Named[name] = resolveContextPaths(root, context) + contexts.Named[name] = resolveContextPaths(dir, context) } } @@ -112,12 +112,12 @@ func parseContextFile(root string, data []byte) (Contexts, error) { func readAndParseContextFileFromURI(path string) (Contexts, error) { var bytes []byte var err error - var root string + var dir string if strings.HasPrefix(path, "https:") || strings.HasPrefix(path, "http:") { bytes, err = readContextFileFromNetwork(path) } else { - root = filepath.Dir(path) + dir = filepath.Dir(path) bytes, err = readContextFileFromFilePath(path) } @@ -125,7 +125,7 @@ func readAndParseContextFileFromURI(path string) (Contexts, error) { return Contexts{}, fmt.Errorf("failed to read context file: %w", err) } - return parseContextFile(root, bytes) + return parseContextFile(dir, bytes) } // LoadContexts reads the context file and returns the contexts. It starts by reading from current @@ -145,22 +145,24 @@ func LoadContexts() (Contexts, error) { return contexts, nil } -func resolveContextPaths(root string, context Context) Context { +func resolveContextPaths(dir string, context Context) Context { clone := context.Clone() - clone.Build.Dockerfile = resolvePath(root, context.Build.Dockerfile) - clone.Build.Context = resolvePath(root, clone.Build.Context) + clone.Build.Dockerfile = resolvePath(dir, context.Build.Dockerfile) + clone.Build.Context = resolvePath(dir, clone.Build.Context) for key, value := range clone.Mounts { - clone.Mounts[resolvePath(root, key)] = value + clone.Mounts[resolvePath(dir, key)] = value } return clone } -func resolvePath(root, path string) string { +func resolvePath(dir, path string) string { if path == "" { return "" } - if strings.HasPrefix(path, "/") { + + if filepath.IsAbs(path) { return path } - return filepath.Join(root, path) + + return filepath.Join(dir, path) } diff --git a/src/internal/docker/cli.go b/src/internal/docker/cli.go index 0923175..12cd6bb 100644 --- a/src/internal/docker/cli.go +++ b/src/internal/docker/cli.go @@ -12,6 +12,7 @@ import ( "github.com/mitchellh/go-homedir" yey "github.com/silphid/yey/src/internal" + "github.com/silphid/yey/src/internal/logging" ) func Start(ctx context.Context, yeyCtx yey.Context, containerName string) error { @@ -46,8 +47,17 @@ func Remove(ctx context.Context, containerName string) error { return attachStdPipes(exec.CommandContext(ctx, "docker", "rm", "-v", containerName)).Run() } -func Build(ctx context.Context, dockerPath string, imageName string, buildArgs map[string]string, context string) error { - args := []string{"build", "-f", dockerPath, "-t", imageName} +func Build(ctx context.Context, dockerPath string, imageTag string, buildArgs map[string]string, context string) error { + exists, err := imageExists(ctx, imageTag) + if err != nil { + return fmt.Errorf("failed to look up image tag %q: %w", imageTag, err) + } + if exists { + logging.Log("found prebuilt image: %q: skipping build step", imageTag) + return nil + } + + args := []string{"build", "-f", dockerPath, "-t", imageTag} for key, value := range buildArgs { args = append(args, "--build-arg", fmt.Sprintf("%s=%q", key, value)) } @@ -71,6 +81,14 @@ func ListContainers(ctx context.Context) ([]string, error) { return newlines.Split(string(output), -1), nil } +func imageExists(ctx context.Context, tag string) (bool, error) { + output, err := exec.CommandContext(ctx, "docker", "image", "inspect", tag).Output() + if err != nil { + return false, err + } + return string(bytes.TrimSpace(output)) != "[]", nil +} + func getContainerStatus(ctx context.Context, name string) (string, error) { cmd := exec.CommandContext(ctx, "docker", "inspect", name, "--format", "{{.State.Status}}") From 808e33f3a190af5c1f0b3d70d045857a617b97dc Mon Sep 17 00:00:00 2001 From: davidmdm Date: Fri, 28 May 2021 14:17:37 -0400 Subject: [PATCH 5/5] clone build.context --- src/internal/context.go | 3 +++ src/internal/docker/cli.go | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/internal/context.go b/src/internal/context.go index 9cff009..c8bc401 100644 --- a/src/internal/context.go +++ b/src/internal/context.go @@ -63,6 +63,9 @@ func (c Context) Merge(source Context) Context { if source.Build.Dockerfile != "" { merged.Build.Dockerfile = source.Build.Dockerfile } + if source.Build.Context != "" { + merged.Build.Context = source.Build.Context + } for key, value := range source.Build.Args { merged.Build.Args[key] = value } diff --git a/src/internal/docker/cli.go b/src/internal/docker/cli.go index 12cd6bb..3dcc8a2 100644 --- a/src/internal/docker/cli.go +++ b/src/internal/docker/cli.go @@ -83,10 +83,13 @@ func ListContainers(ctx context.Context) ([]string, error) { func imageExists(ctx context.Context, tag string) (bool, error) { output, err := exec.CommandContext(ctx, "docker", "image", "inspect", tag).Output() + if string(bytes.TrimSpace(output)) == "[]" { + return false, nil + } if err != nil { return false, err } - return string(bytes.TrimSpace(output)) != "[]", nil + return true, nil } func getContainerStatus(ctx context.Context, name string) (string, error) {