diff --git a/go.mod b/go.mod index 822254d..36f17e4 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.16 require ( github.com/AlecAivazis/survey/v2 v2.2.12 github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 + github.com/TwinProduction/go-color v1.0.0 // indirect github.com/arsham/figurine v1.0.1 github.com/arsham/rainbow v1.1.1 github.com/go-test/deep v1.0.7 diff --git a/go.sum b/go.sum index 4dffd1f..b31b171 100644 --- a/go.sum +++ b/go.sum @@ -18,6 +18,8 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw= github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/TwinProduction/go-color v1.0.0 h1:8n59tqmLmt8jyRsY44RPy2ixPDDw0FcVoAhlYeyz3Jw= +github.com/TwinProduction/go-color v1.0.0/go.mod h1:5hWpSyT+mmKPjCwPNEruBW5Dkbs/2PwOuU468ntEXNQ= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= diff --git a/src/cmd/prompts.go b/src/cmd/prompts.go index 7368710..54cdaef 100644 --- a/src/cmd/prompts.go +++ b/src/cmd/prompts.go @@ -23,7 +23,7 @@ func GetOrPromptContextNames(contexts yey.Contexts, names []string, lastNames [] continue } prompt := &survey.Select{ - Message: fmt.Sprintf("Select %s:", contexts.Layers[i].Name), + Message: fmt.Sprintf("Select %s", contexts.Layers[i].Name), Options: availableNames[i], } if i < len(lastNames) { @@ -38,3 +38,18 @@ func GetOrPromptContextNames(contexts yey.Contexts, names []string, lastNames [] return names, nil } + +// Prompts user to multi-select among given images +func PromptImageNames(allImages []string) ([]string, error) { + + prompt := &survey.MultiSelect{ + Message: "Select images to pull", + Options: allImages, + } + selectedImages := []string{} + if err := survey.AskOne(prompt, &selectedImages); err != nil { + return nil, err + } + + return selectedImages, nil +} diff --git a/src/cmd/pull/pull.go b/src/cmd/pull/pull.go new file mode 100644 index 0000000..480248c --- /dev/null +++ b/src/cmd/pull/pull.go @@ -0,0 +1,62 @@ +package pull + +import ( + "context" + "fmt" + "os" + + "github.com/spf13/cobra" + + "github.com/TwinProduction/go-color" + "github.com/silphid/yey/src/cmd" + yey "github.com/silphid/yey/src/internal" + "github.com/silphid/yey/src/internal/docker" +) + +type pullOptions struct { + all bool +} + +// New creates a cobra command +func New() *cobra.Command { + var options pullOptions + + cmd := &cobra.Command{ + Use: "pull", + Short: "Pull image(s) from registry", + Args: cobra.ArbitraryArgs, + RunE: func(cmd *cobra.Command, args []string) error { + return run(cmd.Context(), args, options) + }, + } + + cmd.Flags().BoolVarP(&options.all, "all", "a", false, "pull all images") + + return cmd +} + +func run(ctx context.Context, names []string, options pullOptions) error { + contexts, err := yey.LoadContexts() + if err != nil { + return err + } + + // Determine which images to pull + images := contexts.GetAllImages() + if !options.all { + images, err = cmd.PromptImageNames(images) + if err != nil { + return fmt.Errorf("failed to prompt images to pull: %w", err) + } + } + + // Pull selected images + for _, image := range images { + fmt.Fprintf(os.Stderr, color.Ize(color.Green, "Pulling %s\n"), image) + if err := docker.Pull(ctx, image); err != nil { + return err + } + } + + return nil +} diff --git a/src/internal/contexts.go b/src/internal/contexts.go index 4a4cc5a..02c7da2 100644 --- a/src/internal/contexts.go +++ b/src/internal/contexts.go @@ -44,6 +44,31 @@ func (c Contexts) GetNamesInAllLayers() [][]string { return names } +// GetAllImages returns the list of image names referenced in all contexts +func (c Contexts) GetAllImages() []string { + namesMap := make(map[string]struct{}) + + if c.Context.Image != "" { + namesMap[c.Context.Image] = struct{}{} + } + + for _, layer := range c.Layers { + for _, ctx := range layer.Contexts { + if ctx.Image != "" { + namesMap[ctx.Image] = struct{}{} + } + } + } + + // Sort + sortedNames := make([]string, 0, len(namesMap)) + for name := range namesMap { + sortedNames = append(sortedNames, name) + } + sort.Strings(sortedNames) + return sortedNames +} + // GetContext returns context with given name (or base context, if name is "base") and // variant (or no variant, if variant name is "") func (c Contexts) GetContext(names []string) (Context, error) { diff --git a/src/internal/docker/cli.go b/src/internal/docker/cli.go index f2ece78..c3dd23e 100644 --- a/src/internal/docker/cli.go +++ b/src/internal/docker/cli.go @@ -70,6 +70,10 @@ func RemoveMany(ctx context.Context, containers []string, options RemoveOptions) return run(ctx, args...) } +func Pull(ctx context.Context, image string) error { + return run(ctx, "pull", image) +} + func Build(ctx context.Context, dockerPath string, imageTag string, buildArgs map[string]string, context string) error { exists, err := imageExists(ctx, imageTag) if err != nil { diff --git a/src/internal/verbose.go b/src/internal/verbose.go index 689c177..a7f583b 100644 --- a/src/internal/verbose.go +++ b/src/internal/verbose.go @@ -3,6 +3,8 @@ package yey import ( "fmt" "os" + + "github.com/TwinProduction/go-color" ) var ( @@ -11,6 +13,6 @@ var ( func Log(format string, a ...interface{}) { if IsVerbose { - fmt.Fprintf(os.Stderr, format+"\n", a...) + fmt.Fprintf(os.Stderr, color.Ize(color.Yellow, format+"\n"), a...) } } diff --git a/src/main.go b/src/main.go index c6a02f0..5a919f0 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/pull" "github.com/silphid/yey/src/cmd/remove" getcontainers "github.com/silphid/yey/src/cmd/get/containers" @@ -35,6 +36,7 @@ func main() { rootCmd.AddCommand(versioning.New(version)) rootCmd.AddCommand(tidy.New()) rootCmd.AddCommand(remove.New()) + rootCmd.AddCommand(pull.New()) getCmd := get.New() getCmd.AddCommand(getcontext.New())