diff --git a/cmd/compose/alpha.go b/cmd/compose/alpha.go index 99359360c54..3e4c41bb692 100644 --- a/cmd/compose/alpha.go +++ b/cmd/compose/alpha.go @@ -15,12 +15,13 @@ package compose import ( + "github.com/docker/cli/cli/command" "github.com/docker/compose/v2/pkg/api" "github.com/spf13/cobra" ) // alphaCommand groups all experimental subcommands -func alphaCommand(p *ProjectOptions, backend api.Service) *cobra.Command { +func alphaCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command { cmd := &cobra.Command{ Short: "Experimental commands", Use: "alpha [COMMAND]", @@ -30,9 +31,9 @@ func alphaCommand(p *ProjectOptions, backend api.Service) *cobra.Command { }, } cmd.AddCommand( - watchCommand(p, backend), - vizCommand(p, backend), - publishCommand(p, backend), + watchCommand(p, dockerCli, backend), + vizCommand(p, dockerCli, backend), + publishCommand(p, dockerCli, backend), ) return cmd } diff --git a/cmd/compose/build.go b/cmd/compose/build.go index 00b3ba06b1b..94b950f4222 100644 --- a/cmd/compose/build.go +++ b/cmd/compose/build.go @@ -26,6 +26,7 @@ import ( "github.com/compose-spec/compose-go/loader" "github.com/compose-spec/compose-go/types" buildx "github.com/docker/buildx/util/progress" + "github.com/docker/cli/cli/command" cliopts "github.com/docker/cli/opts" ui "github.com/docker/compose/v2/pkg/progress" "github.com/spf13/cobra" @@ -72,7 +73,7 @@ func (opts buildOptions) toAPIBuildOptions(services []string) (api.BuildOptions, }, nil } -func buildCommand(p *ProjectOptions, backend api.Service) *cobra.Command { +func buildCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command { opts := buildOptions{ ProjectOptions: p, } @@ -97,9 +98,9 @@ func buildCommand(p *ProjectOptions, backend api.Service) *cobra.Command { if cmd.Flags().Changed("progress") && opts.ssh == "" { fmt.Fprint(os.Stderr, "--progress is a global compose flag, better use `docker compose --progress xx build ...") } - return runBuild(ctx, backend, opts, args) + return runBuild(ctx, dockerCli, backend, opts, args) }), - ValidArgsFunction: completeServiceNames(p), + ValidArgsFunction: completeServiceNames(dockerCli, p), } cmd.Flags().BoolVar(&opts.push, "push", false, "Push service images.") cmd.Flags().BoolVarP(&opts.quiet, "quiet", "q", false, "Don't print anything to STDOUT") @@ -123,8 +124,8 @@ func buildCommand(p *ProjectOptions, backend api.Service) *cobra.Command { return cmd } -func runBuild(ctx context.Context, backend api.Service, opts buildOptions, services []string) error { - project, err := opts.ToProject(services, cli.WithResolvedPaths(true)) +func runBuild(ctx context.Context, dockerCli command.Cli, backend api.Service, opts buildOptions, services []string) error { + project, err := opts.ToProject(dockerCli, services, cli.WithResolvedPaths(true)) if err != nil { return err } diff --git a/cmd/compose/completion.go b/cmd/compose/completion.go index f5461ff7712..5423cd6441b 100644 --- a/cmd/compose/completion.go +++ b/cmd/compose/completion.go @@ -20,6 +20,7 @@ import ( "sort" "strings" + "github.com/docker/cli/cli/command" "github.com/docker/compose/v2/pkg/api" "github.com/spf13/cobra" ) @@ -33,9 +34,10 @@ func noCompletion() validArgsFn { } } -func completeServiceNames(p *ProjectOptions) validArgsFn { +func completeServiceNames(dockerCli command.Cli, p *ProjectOptions) validArgsFn { return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - project, err := p.ToProject(nil) + p.Offline = true + project, err := p.ToProject(dockerCli, nil) if err != nil { return nil, cobra.ShellCompDirectiveNoFileComp } @@ -67,9 +69,10 @@ func completeProjectNames(backend api.Service) func(cmd *cobra.Command, args []s } } -func completeProfileNames(p *ProjectOptions) validArgsFn { +func completeProfileNames(dockerCli command.Cli, p *ProjectOptions) validArgsFn { return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - project, err := p.ToProject(nil) + p.Offline = true + project, err := p.ToProject(dockerCli, nil) if err != nil { return nil, cobra.ShellCompDirectiveNoFileComp } diff --git a/cmd/compose/compose.go b/cmd/compose/compose.go index 8df84df21c3..9430dcf9db5 100644 --- a/cmd/compose/compose.go +++ b/cmd/compose/compose.go @@ -26,18 +26,16 @@ import ( "strings" "syscall" - buildx "github.com/docker/buildx/util/progress" - - "github.com/compose-spec/compose-go/dotenv" - "github.com/docker/cli/cli/command" - "github.com/docker/compose/v2/pkg/remote" - "github.com/compose-spec/compose-go/cli" + "github.com/compose-spec/compose-go/dotenv" "github.com/compose-spec/compose-go/types" composegoutils "github.com/compose-spec/compose-go/utils" "github.com/docker/buildx/util/logutil" + buildx "github.com/docker/buildx/util/progress" dockercli "github.com/docker/cli/cli" "github.com/docker/cli/cli-plugins/manager" + "github.com/docker/cli/cli/command" + "github.com/docker/compose/v2/pkg/remote" "github.com/morikuni/aec" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -119,6 +117,7 @@ type ProjectOptions struct { EnvFiles []string Compatibility bool Progress string + Offline bool } // ProjectFunc does stuff within a types.Project @@ -128,14 +127,14 @@ type ProjectFunc func(ctx context.Context, project *types.Project) error type ProjectServicesFunc func(ctx context.Context, project *types.Project, services []string) error // WithProject creates a cobra run command from a ProjectFunc based on configured project options and selected services -func (o *ProjectOptions) WithProject(fn ProjectFunc) func(cmd *cobra.Command, args []string) error { - return o.WithServices(func(ctx context.Context, project *types.Project, services []string) error { +func (o *ProjectOptions) WithProject(fn ProjectFunc, dockerCli command.Cli) func(cmd *cobra.Command, args []string) error { + return o.WithServices(dockerCli, func(ctx context.Context, project *types.Project, services []string) error { return fn(ctx, project) }) } // WithServices creates a cobra run command from a ProjectFunc based on configured project options and selected services -func (o *ProjectOptions) WithServices(fn ProjectServicesFunc) func(cmd *cobra.Command, args []string) error { +func (o *ProjectOptions) WithServices(dockerCli command.Cli, fn ProjectServicesFunc) func(cmd *cobra.Command, args []string) error { return Adapt(func(ctx context.Context, args []string) error { options := []cli.ProjectOptionsFn{ cli.WithResolvedPaths(true), @@ -143,19 +142,7 @@ func (o *ProjectOptions) WithServices(fn ProjectServicesFunc) func(cmd *cobra.Co cli.WithContext(ctx), } - enabled, err := remote.GitRemoteLoaderEnabled() - if err != nil { - return err - } - if enabled { - git, err := remote.NewGitRemoteLoader() - if err != nil { - return err - } - options = append(options, cli.WithResourceLoader(git)) - } - - project, err := o.ToProject(args, options...) + project, err := o.ToProject(dockerCli, args, options...) if err != nil { return err } @@ -176,11 +163,11 @@ func (o *ProjectOptions) addProjectFlags(f *pflag.FlagSet) { _ = f.MarkHidden("workdir") } -func (o *ProjectOptions) projectOrName(services ...string) (*types.Project, string, error) { +func (o *ProjectOptions) projectOrName(dockerCli command.Cli, services ...string) (*types.Project, string, error) { name := o.ProjectName var project *types.Project if len(o.ConfigPaths) > 0 || o.ProjectName == "" { - p, err := o.ToProject(services, cli.WithDiscardEnvFile) + p, err := o.ToProject(dockerCli, services, cli.WithDiscardEnvFile) if err != nil { envProjectName := os.Getenv(ComposeProjectName) if envProjectName != "" { @@ -194,7 +181,7 @@ func (o *ProjectOptions) projectOrName(services ...string) (*types.Project, stri return project, name, nil } -func (o *ProjectOptions) toProjectName() (string, error) { +func (o *ProjectOptions) toProjectName(dockerCli command.Cli) (string, error) { if o.ProjectName != "" { return o.ProjectName, nil } @@ -204,14 +191,22 @@ func (o *ProjectOptions) toProjectName() (string, error) { return envProjectName, nil } - project, err := o.ToProject(nil) + project, err := o.ToProject(dockerCli, nil) if err != nil { return "", err } return project.Name, nil } -func (o *ProjectOptions) ToProject(services []string, po ...cli.ProjectOptionsFn) (*types.Project, error) { +func (o *ProjectOptions) ToProject(dockerCli command.Cli, services []string, po ...cli.ProjectOptionsFn) (*types.Project, error) { + if !o.Offline { + var err error + po, err = o.configureRemoteLoaders(dockerCli, po) + if err != nil { + return nil, err + } + } + options, err := o.toProjectOptions(po...) if err != nil { return nil, compose.WrapComposeError(err) @@ -256,6 +251,33 @@ func (o *ProjectOptions) ToProject(services []string, po ...cli.ProjectOptionsFn return project, err } +func (o *ProjectOptions) configureRemoteLoaders(dockerCli command.Cli, po []cli.ProjectOptionsFn) ([]cli.ProjectOptionsFn, error) { + enabled, err := remote.GitRemoteLoaderEnabled() + if err != nil { + return nil, err + } + if enabled { + git, err := remote.NewGitRemoteLoader(o.Offline) + if err != nil { + return nil, err + } + po = append(po, cli.WithResourceLoader(git)) + } + + enabled, err = remote.OCIRemoteLoaderEnabled() + if err != nil { + return nil, err + } + if enabled { + git, err := remote.NewOCIRemoteLoader(dockerCli, o.Offline) + if err != nil { + return nil, err + } + po = append(po, cli.WithResourceLoader(git)) + } + return po, nil +} + func (o *ProjectOptions) toProjectOptions(po ...cli.ProjectOptionsFn) (*cli.ProjectOptions, error) { return cli.NewProjectOptions(o.ConfigPaths, append(po, @@ -429,32 +451,32 @@ func RootCommand(dockerCli command.Cli, backend api.Service) *cobra.Command { // c.AddCommand( upCommand(&opts, dockerCli, backend), - downCommand(&opts, backend), - startCommand(&opts, backend), - restartCommand(&opts, backend), - stopCommand(&opts, backend), + downCommand(&opts, dockerCli, backend), + startCommand(&opts, dockerCli, backend), + restartCommand(&opts, dockerCli, backend), + stopCommand(&opts, dockerCli, backend), psCommand(&opts, dockerCli, backend), listCommand(dockerCli, backend), logsCommand(&opts, dockerCli, backend), configCommand(&opts, dockerCli, backend), - killCommand(&opts, backend), + killCommand(&opts, dockerCli, backend), runCommand(&opts, dockerCli, backend), - removeCommand(&opts, backend), + removeCommand(&opts, dockerCli, backend), execCommand(&opts, dockerCli, backend), - pauseCommand(&opts, backend), - unpauseCommand(&opts, backend), + pauseCommand(&opts, dockerCli, backend), + unpauseCommand(&opts, dockerCli, backend), topCommand(&opts, dockerCli, backend), eventsCommand(&opts, dockerCli, backend), portCommand(&opts, dockerCli, backend), imagesCommand(&opts, dockerCli, backend), versionCommand(dockerCli), - buildCommand(&opts, backend), - pushCommand(&opts, backend), - pullCommand(&opts, backend), - createCommand(&opts, backend), - copyCommand(&opts, backend), - waitCommand(&opts, backend), - alphaCommand(&opts, backend), + buildCommand(&opts, dockerCli, backend), + pushCommand(&opts, dockerCli, backend), + pullCommand(&opts, dockerCli, backend), + createCommand(&opts, dockerCli, backend), + copyCommand(&opts, dockerCli, backend), + waitCommand(&opts, dockerCli, backend), + alphaCommand(&opts, dockerCli, backend), ) c.Flags().SetInterspersed(false) @@ -477,7 +499,7 @@ func RootCommand(dockerCli command.Cli, backend api.Service) *cobra.Command { // ) c.RegisterFlagCompletionFunc( //nolint:errcheck "profile", - completeProfileNames(&opts), + completeProfileNames(dockerCli, &opts), ) c.Flags().StringVar(&ansi, "ansi", "auto", `Control when to print ANSI control characters ("never"|"always"|"auto")`) diff --git a/cmd/compose/config.go b/cmd/compose/config.go index df957b8d6dc..bc17baa9195 100644 --- a/cmd/compose/config.go +++ b/cmd/compose/config.go @@ -26,7 +26,7 @@ import ( "github.com/compose-spec/compose-go/cli" "github.com/compose-spec/compose-go/types" - "github.com/docker/compose/v2/pkg/remote" + "github.com/docker/cli/cli/command" "github.com/spf13/cobra" "github.com/docker/compose/v2/pkg/api" @@ -50,24 +50,18 @@ type configOptions struct { noConsistency bool } -func (o *configOptions) ToProject(ctx context.Context, services []string) (*types.Project, error) { - git, err := remote.NewGitRemoteLoader() - if err != nil { - return nil, err - } - - return o.ProjectOptions.ToProject(services, +func (o *configOptions) ToProject(ctx context.Context, dockerCli command.Cli, services []string) (*types.Project, error) { + return o.ProjectOptions.ToProject(dockerCli, services, cli.WithInterpolation(!o.noInterpolate), cli.WithResolvedPaths(!o.noResolvePath), cli.WithNormalization(!o.noNormalize), cli.WithConsistency(!o.noConsistency), cli.WithDefaultProfiles(o.Profiles...), cli.WithDiscardEnvFile, - cli.WithContext(ctx), - cli.WithResourceLoader(git)) + cli.WithContext(ctx)) } -func configCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *cobra.Command { +func configCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command { opts := configOptions{ ProjectOptions: p, } @@ -90,24 +84,24 @@ func configCommand(p *ProjectOptions, streams api.Streams, backend api.Service) }), RunE: Adapt(func(ctx context.Context, args []string) error { if opts.services { - return runServices(ctx, streams, opts) + return runServices(ctx, dockerCli, opts) } if opts.volumes { - return runVolumes(ctx, streams, opts) + return runVolumes(ctx, dockerCli, opts) } if opts.hash != "" { - return runHash(ctx, streams, opts) + return runHash(ctx, dockerCli, opts) } if opts.profiles { - return runProfiles(ctx, streams, opts, args) + return runProfiles(ctx, dockerCli, opts, args) } if opts.images { - return runConfigImages(ctx, streams, opts, args) + return runConfigImages(ctx, dockerCli, opts, args) } - return runConfig(ctx, streams, backend, opts, args) + return runConfig(ctx, dockerCli, backend, opts, args) }), - ValidArgsFunction: completeServiceNames(p), + ValidArgsFunction: completeServiceNames(dockerCli, p), } flags := cmd.Flags() flags.StringVar(&opts.Format, "format", "yaml", "Format the output. Values: [yaml | json]") @@ -128,9 +122,9 @@ func configCommand(p *ProjectOptions, streams api.Streams, backend api.Service) return cmd } -func runConfig(ctx context.Context, streams api.Streams, backend api.Service, opts configOptions, services []string) error { +func runConfig(ctx context.Context, dockerCli command.Cli, backend api.Service, opts configOptions, services []string) error { var content []byte - project, err := opts.ToProject(ctx, services) + project, err := opts.ToProject(ctx, dockerCli, services) if err != nil { return err } @@ -155,38 +149,38 @@ func runConfig(ctx context.Context, streams api.Streams, backend api.Service, op if opts.Output != "" && len(content) > 0 { return os.WriteFile(opts.Output, content, 0o666) } - _, err = fmt.Fprint(streams.Out(), string(content)) + _, err = fmt.Fprint(dockerCli.Out(), string(content)) return err } -func runServices(ctx context.Context, streams api.Streams, opts configOptions) error { - project, err := opts.ToProject(ctx, nil) +func runServices(ctx context.Context, dockerCli command.Cli, opts configOptions) error { + project, err := opts.ToProject(ctx, dockerCli, nil) if err != nil { return err } return project.WithServices(project.ServiceNames(), func(s types.ServiceConfig) error { - fmt.Fprintln(streams.Out(), s.Name) + fmt.Fprintln(dockerCli.Out(), s.Name) return nil }) } -func runVolumes(ctx context.Context, streams api.Streams, opts configOptions) error { - project, err := opts.ToProject(ctx, nil) +func runVolumes(ctx context.Context, dockerCli command.Cli, opts configOptions) error { + project, err := opts.ToProject(ctx, dockerCli, nil) if err != nil { return err } for n := range project.Volumes { - fmt.Fprintln(streams.Out(), n) + fmt.Fprintln(dockerCli.Out(), n) } return nil } -func runHash(ctx context.Context, streams api.Streams, opts configOptions) error { +func runHash(ctx context.Context, dockerCli command.Cli, opts configOptions) error { var services []string if opts.hash != "*" { services = append(services, strings.Split(opts.hash, ",")...) } - project, err := opts.ToProject(ctx, nil) + project, err := opts.ToProject(ctx, dockerCli, nil) if err != nil { return err } @@ -208,14 +202,14 @@ func runHash(ctx context.Context, streams api.Streams, opts configOptions) error if err != nil { return err } - fmt.Fprintf(streams.Out(), "%s %s\n", s.Name, hash) + fmt.Fprintf(dockerCli.Out(), "%s %s\n", s.Name, hash) } return nil } -func runProfiles(ctx context.Context, streams api.Streams, opts configOptions, services []string) error { +func runProfiles(ctx context.Context, dockerCli command.Cli, opts configOptions, services []string) error { set := map[string]struct{}{} - project, err := opts.ToProject(ctx, services) + project, err := opts.ToProject(ctx, dockerCli, services) if err != nil { return err } @@ -230,18 +224,18 @@ func runProfiles(ctx context.Context, streams api.Streams, opts configOptions, s } sort.Strings(profiles) for _, p := range profiles { - fmt.Fprintln(streams.Out(), p) + fmt.Fprintln(dockerCli.Out(), p) } return nil } -func runConfigImages(ctx context.Context, streams api.Streams, opts configOptions, services []string) error { - project, err := opts.ToProject(ctx, services) +func runConfigImages(ctx context.Context, dockerCli command.Cli, opts configOptions, services []string) error { + project, err := opts.ToProject(ctx, dockerCli, services) if err != nil { return err } for _, s := range project.Services { - fmt.Fprintln(streams.Out(), api.GetImageNameOrDefault(s, project.Name)) + fmt.Fprintln(dockerCli.Out(), api.GetImageNameOrDefault(s, project.Name)) } return nil } diff --git a/cmd/compose/cp.go b/cmd/compose/cp.go index c09efd47136..4c346b2a7b7 100644 --- a/cmd/compose/cp.go +++ b/cmd/compose/cp.go @@ -21,6 +21,7 @@ import ( "errors" "github.com/docker/cli/cli" + "github.com/docker/cli/cli/command" "github.com/spf13/cobra" "github.com/docker/compose/v2/pkg/api" @@ -37,7 +38,7 @@ type copyOptions struct { copyUIDGID bool } -func copyCommand(p *ProjectOptions, backend api.Service) *cobra.Command { +func copyCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command { opts := copyOptions{ ProjectOptions: p, } @@ -58,9 +59,9 @@ func copyCommand(p *ProjectOptions, backend api.Service) *cobra.Command { RunE: AdaptCmd(func(ctx context.Context, cmd *cobra.Command, args []string) error { opts.source = args[0] opts.destination = args[1] - return runCopy(ctx, backend, opts) + return runCopy(ctx, dockerCli, backend, opts) }), - ValidArgsFunction: completeServiceNames(p), + ValidArgsFunction: completeServiceNames(dockerCli, p), } flags := copyCmd.Flags() @@ -74,8 +75,8 @@ func copyCommand(p *ProjectOptions, backend api.Service) *cobra.Command { return copyCmd } -func runCopy(ctx context.Context, backend api.Service, opts copyOptions) error { - name, err := opts.toProjectName() +func runCopy(ctx context.Context, dockerCli command.Cli, backend api.Service, opts copyOptions) error { + name, err := opts.toProjectName(dockerCli) if err != nil { return err } diff --git a/cmd/compose/create.go b/cmd/compose/create.go index 0efa7989d81..e19d4fceab6 100644 --- a/cmd/compose/create.go +++ b/cmd/compose/create.go @@ -24,6 +24,7 @@ import ( "time" "github.com/compose-spec/compose-go/types" + "github.com/docker/cli/cli/command" "github.com/spf13/cobra" "github.com/docker/compose/v2/pkg/api" @@ -46,7 +47,7 @@ type createOptions struct { scale []string } -func createCommand(p *ProjectOptions, backend api.Service) *cobra.Command { +func createCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command { opts := createOptions{} cmd := &cobra.Command{ Use: "create [OPTIONS] [SERVICE...]", @@ -74,8 +75,8 @@ func createCommand(p *ProjectOptions, backend api.Service) *cobra.Command { Timeout: opts.GetTimeout(), QuietPull: false, }) - }), - ValidArgsFunction: completeServiceNames(p), + }, dockerCli), + ValidArgsFunction: completeServiceNames(dockerCli, p), } flags := cmd.Flags() flags.BoolVar(&opts.Build, "build", false, "Build images before starting containers.") diff --git a/cmd/compose/down.go b/cmd/compose/down.go index 44dea50639d..4ac85f348f1 100644 --- a/cmd/compose/down.go +++ b/cmd/compose/down.go @@ -22,6 +22,7 @@ import ( "os" "time" + "github.com/docker/cli/cli/command" "github.com/docker/compose/v2/pkg/utils" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -39,7 +40,7 @@ type downOptions struct { images string } -func downCommand(p *ProjectOptions, backend api.Service) *cobra.Command { +func downCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command { opts := downOptions{ ProjectOptions: p, } @@ -56,7 +57,7 @@ func downCommand(p *ProjectOptions, backend api.Service) *cobra.Command { return nil }), RunE: Adapt(func(ctx context.Context, args []string) error { - return runDown(ctx, backend, opts, args) + return runDown(ctx, dockerCli, backend, opts, args) }), ValidArgsFunction: noCompletion(), } @@ -76,8 +77,8 @@ func downCommand(p *ProjectOptions, backend api.Service) *cobra.Command { return downCmd } -func runDown(ctx context.Context, backend api.Service, opts downOptions, services []string) error { - project, name, err := opts.projectOrName() +func runDown(ctx context.Context, dockerCli command.Cli, backend api.Service, opts downOptions, services []string) error { + project, name, err := opts.projectOrName(dockerCli) if err != nil { return err } diff --git a/cmd/compose/events.go b/cmd/compose/events.go index 738d8265d37..10ef5b4427e 100644 --- a/cmd/compose/events.go +++ b/cmd/compose/events.go @@ -21,6 +21,7 @@ import ( "encoding/json" "fmt" + "github.com/docker/cli/cli/command" "github.com/docker/compose/v2/pkg/api" "github.com/spf13/cobra" @@ -31,7 +32,7 @@ type eventsOpts struct { json bool } -func eventsCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *cobra.Command { +func eventsCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command { opts := eventsOpts{ composeOptions: &composeOptions{ ProjectOptions: p, @@ -41,17 +42,17 @@ func eventsCommand(p *ProjectOptions, streams api.Streams, backend api.Service) Use: "events [OPTIONS] [SERVICE...]", Short: "Receive real time events from containers.", RunE: Adapt(func(ctx context.Context, args []string) error { - return runEvents(ctx, streams, backend, opts, args) + return runEvents(ctx, dockerCli, backend, opts, args) }), - ValidArgsFunction: completeServiceNames(p), + ValidArgsFunction: completeServiceNames(dockerCli, p), } cmd.Flags().BoolVar(&opts.json, "json", false, "Output events as a stream of json objects") return cmd } -func runEvents(ctx context.Context, streams api.Streams, backend api.Service, opts eventsOpts, services []string) error { - name, err := opts.toProjectName() +func runEvents(ctx context.Context, dockerCli command.Cli, backend api.Service, opts eventsOpts, services []string) error { + name, err := opts.toProjectName(dockerCli) if err != nil { return err } @@ -71,9 +72,9 @@ func runEvents(ctx context.Context, streams api.Streams, backend api.Service, op if err != nil { return err } - fmt.Fprintln(streams.Out(), string(marshal)) + fmt.Fprintln(dockerCli.Out(), string(marshal)) } else { - fmt.Fprintln(streams.Out(), event) + fmt.Fprintln(dockerCli.Out(), event) } return nil }, diff --git a/cmd/compose/exec.go b/cmd/compose/exec.go index a9e2d1ac107..0df79ec3b60 100644 --- a/cmd/compose/exec.go +++ b/cmd/compose/exec.go @@ -21,6 +21,7 @@ import ( "github.com/compose-spec/compose-go/types" "github.com/docker/cli/cli" + "github.com/docker/cli/cli/command" "github.com/docker/compose/v2/pkg/api" "github.com/docker/compose/v2/pkg/compose" "github.com/spf13/cobra" @@ -42,7 +43,7 @@ type execOpts struct { interactive bool } -func execCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *cobra.Command { +func execCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command { opts := execOpts{ composeOptions: &composeOptions{ ProjectOptions: p, @@ -58,9 +59,9 @@ func execCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *c return nil }), RunE: Adapt(func(ctx context.Context, args []string) error { - return runExec(ctx, backend, opts) + return runExec(ctx, dockerCli, backend, opts) }), - ValidArgsFunction: completeServiceNames(p), + ValidArgsFunction: completeServiceNames(dockerCli, p), } runCmd.Flags().BoolVarP(&opts.detach, "detach", "d", false, "Detached mode: Run command in the background.") @@ -68,7 +69,7 @@ func execCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *c runCmd.Flags().IntVar(&opts.index, "index", 0, "index of the container if service has multiple replicas") runCmd.Flags().BoolVarP(&opts.privileged, "privileged", "", false, "Give extended privileges to the process.") runCmd.Flags().StringVarP(&opts.user, "user", "u", "", "Run the command as this user.") - runCmd.Flags().BoolVarP(&opts.noTty, "no-TTY", "T", !streams.Out().IsTerminal(), "Disable pseudo-TTY allocation. By default `docker compose exec` allocates a TTY.") + runCmd.Flags().BoolVarP(&opts.noTty, "no-TTY", "T", !dockerCli.Out().IsTerminal(), "Disable pseudo-TTY allocation. By default `docker compose exec` allocates a TTY.") runCmd.Flags().StringVarP(&opts.workingDir, "workdir", "w", "", "Path to workdir directory for this command.") runCmd.Flags().BoolVarP(&opts.interactive, "interactive", "i", true, "Keep STDIN open even if not attached.") @@ -80,8 +81,8 @@ func execCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *c return runCmd } -func runExec(ctx context.Context, backend api.Service, opts execOpts) error { - projectName, err := opts.toProjectName() +func runExec(ctx context.Context, dockerCli command.Cli, backend api.Service, opts execOpts) error { + projectName, err := opts.toProjectName(dockerCli) if err != nil { return err } diff --git a/cmd/compose/images.go b/cmd/compose/images.go index 7bb9ea3678c..d10700767a4 100644 --- a/cmd/compose/images.go +++ b/cmd/compose/images.go @@ -23,6 +23,7 @@ import ( "sort" "strings" + "github.com/docker/cli/cli/command" "github.com/docker/docker/pkg/stringid" "github.com/docker/go-units" "github.com/spf13/cobra" @@ -38,7 +39,7 @@ type imageOptions struct { Format string } -func imagesCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *cobra.Command { +func imagesCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command { opts := imageOptions{ ProjectOptions: p, } @@ -46,17 +47,17 @@ func imagesCommand(p *ProjectOptions, streams api.Streams, backend api.Service) Use: "images [OPTIONS] [SERVICE...]", Short: "List images used by the created containers", RunE: Adapt(func(ctx context.Context, args []string) error { - return runImages(ctx, streams, backend, opts, args) + return runImages(ctx, dockerCli, backend, opts, args) }), - ValidArgsFunction: completeServiceNames(p), + ValidArgsFunction: completeServiceNames(dockerCli, p), } imgCmd.Flags().StringVar(&opts.Format, "format", "table", "Format the output. Values: [table | json].") imgCmd.Flags().BoolVarP(&opts.Quiet, "quiet", "q", false, "Only display IDs") return imgCmd } -func runImages(ctx context.Context, streams api.Streams, backend api.Service, opts imageOptions, services []string) error { - projectName, err := opts.toProjectName() +func runImages(ctx context.Context, dockerCli command.Cli, backend api.Service, opts imageOptions, services []string) error { + projectName, err := opts.toProjectName(dockerCli) if err != nil { return err } @@ -80,7 +81,7 @@ func runImages(ctx context.Context, streams api.Streams, backend api.Service, op } } for _, img := range ids { - fmt.Fprintln(streams.Out(), img) + fmt.Fprintln(dockerCli.Out(), img) } return nil } @@ -89,7 +90,7 @@ func runImages(ctx context.Context, streams api.Streams, backend api.Service, op return images[i].ContainerName < images[j].ContainerName }) - return formatter.Print(images, opts.Format, streams.Out(), + return formatter.Print(images, opts.Format, dockerCli.Out(), func(w io.Writer) { for _, img := range images { id := stringid.TruncateID(img.ID) diff --git a/cmd/compose/kill.go b/cmd/compose/kill.go index 629db5f63bc..d38201cbc9d 100644 --- a/cmd/compose/kill.go +++ b/cmd/compose/kill.go @@ -20,6 +20,7 @@ import ( "context" "os" + "github.com/docker/cli/cli/command" "github.com/spf13/cobra" "github.com/docker/compose/v2/pkg/api" @@ -32,7 +33,7 @@ type killOptions struct { signal string } -func killCommand(p *ProjectOptions, backend api.Service) *cobra.Command { +func killCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command { opts := killOptions{ ProjectOptions: p, } @@ -40,9 +41,9 @@ func killCommand(p *ProjectOptions, backend api.Service) *cobra.Command { Use: "kill [OPTIONS] [SERVICE...]", Short: "Force stop service containers.", RunE: Adapt(func(ctx context.Context, args []string) error { - return runKill(ctx, backend, opts, args) + return runKill(ctx, dockerCli, backend, opts, args) }), - ValidArgsFunction: completeServiceNames(p), + ValidArgsFunction: completeServiceNames(dockerCli, p), } flags := cmd.Flags() @@ -53,8 +54,8 @@ func killCommand(p *ProjectOptions, backend api.Service) *cobra.Command { return cmd } -func runKill(ctx context.Context, backend api.Service, opts killOptions, services []string) error { - project, name, err := opts.projectOrName(services...) +func runKill(ctx context.Context, dockerCli command.Cli, backend api.Service, opts killOptions, services []string) error { + project, name, err := opts.projectOrName(dockerCli, services...) if err != nil { return err } diff --git a/cmd/compose/list.go b/cmd/compose/list.go index 4eac06da8ab..882d5323c37 100644 --- a/cmd/compose/list.go +++ b/cmd/compose/list.go @@ -22,6 +22,7 @@ import ( "io" "strings" + "github.com/docker/cli/cli/command" "github.com/docker/compose/v2/cmd/formatter" "github.com/docker/cli/opts" @@ -37,13 +38,13 @@ type lsOptions struct { Filter opts.FilterOpt } -func listCommand(streams api.Streams, backend api.Service) *cobra.Command { +func listCommand(dockerCli command.Cli, backend api.Service) *cobra.Command { lsOpts := lsOptions{Filter: opts.NewFilterOpt()} lsCmd := &cobra.Command{ Use: "ls [OPTIONS]", Short: "List running compose projects", RunE: Adapt(func(ctx context.Context, args []string) error { - return runList(ctx, streams, backend, lsOpts) + return runList(ctx, dockerCli, backend, lsOpts) }), Args: cobra.NoArgs, ValidArgsFunction: noCompletion(), @@ -60,7 +61,7 @@ var acceptedListFilters = map[string]bool{ "name": true, } -func runList(ctx context.Context, streams api.Streams, backend api.Service, lsOpts lsOptions) error { +func runList(ctx context.Context, dockerCli command.Cli, backend api.Service, lsOpts lsOptions) error { filters := lsOpts.Filter.Value() err := filters.Validate(acceptedListFilters) if err != nil { @@ -73,7 +74,7 @@ func runList(ctx context.Context, streams api.Streams, backend api.Service, lsOp } if lsOpts.Quiet { for _, s := range stackList { - fmt.Fprintln(streams.Out(), s.Name) + fmt.Fprintln(dockerCli.Out(), s.Name) } return nil } @@ -90,7 +91,7 @@ func runList(ctx context.Context, streams api.Streams, backend api.Service, lsOp } view := viewFromStackList(stackList) - return formatter.Print(view, lsOpts.Format, streams.Out(), func(w io.Writer) { + return formatter.Print(view, lsOpts.Format, dockerCli.Out(), func(w io.Writer) { for _, stack := range view { _, _ = fmt.Fprintf(w, "%s\t%s\t%s\n", stack.Name, stack.Status, stack.ConfigFiles) } diff --git a/cmd/compose/logs.go b/cmd/compose/logs.go index 6caa4cff6f7..a121deb9d30 100644 --- a/cmd/compose/logs.go +++ b/cmd/compose/logs.go @@ -19,6 +19,7 @@ package compose import ( "context" + "github.com/docker/cli/cli/command" "github.com/spf13/cobra" "github.com/docker/compose/v2/cmd/formatter" @@ -37,7 +38,7 @@ type logsOptions struct { timestamps bool } -func logsCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *cobra.Command { +func logsCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command { opts := logsOptions{ ProjectOptions: p, } @@ -45,9 +46,9 @@ func logsCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *c Use: "logs [OPTIONS] [SERVICE...]", Short: "View output from containers", RunE: Adapt(func(ctx context.Context, args []string) error { - return runLogs(ctx, streams, backend, opts, args) + return runLogs(ctx, dockerCli, backend, opts, args) }), - ValidArgsFunction: completeServiceNames(p), + ValidArgsFunction: completeServiceNames(dockerCli, p), } flags := logsCmd.Flags() flags.BoolVarP(&opts.follow, "follow", "f", false, "Follow log output.") @@ -60,12 +61,12 @@ func logsCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *c return logsCmd } -func runLogs(ctx context.Context, streams api.Streams, backend api.Service, opts logsOptions, services []string) error { - project, name, err := opts.projectOrName(services...) +func runLogs(ctx context.Context, dockerCli command.Cli, backend api.Service, opts logsOptions, services []string) error { + project, name, err := opts.projectOrName(dockerCli, services...) if err != nil { return err } - consumer := formatter.NewLogConsumer(ctx, streams.Out(), streams.Err(), !opts.noColor, !opts.noPrefix, false) + consumer := formatter.NewLogConsumer(ctx, dockerCli.Out(), dockerCli.Err(), !opts.noColor, !opts.noPrefix, false) return backend.Logs(ctx, name, consumer, api.LogOptions{ Project: project, Services: services, diff --git a/cmd/compose/pause.go b/cmd/compose/pause.go index 3d686ee9ed6..acfc0dec51f 100644 --- a/cmd/compose/pause.go +++ b/cmd/compose/pause.go @@ -19,6 +19,7 @@ package compose import ( "context" + "github.com/docker/cli/cli/command" "github.com/spf13/cobra" "github.com/docker/compose/v2/pkg/api" @@ -28,7 +29,7 @@ type pauseOptions struct { *ProjectOptions } -func pauseCommand(p *ProjectOptions, backend api.Service) *cobra.Command { +func pauseCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command { opts := pauseOptions{ ProjectOptions: p, } @@ -36,15 +37,15 @@ func pauseCommand(p *ProjectOptions, backend api.Service) *cobra.Command { Use: "pause [SERVICE...]", Short: "Pause services", RunE: Adapt(func(ctx context.Context, args []string) error { - return runPause(ctx, backend, opts, args) + return runPause(ctx, dockerCli, backend, opts, args) }), - ValidArgsFunction: completeServiceNames(p), + ValidArgsFunction: completeServiceNames(dockerCli, p), } return cmd } -func runPause(ctx context.Context, backend api.Service, opts pauseOptions, services []string) error { - project, name, err := opts.projectOrName(services...) +func runPause(ctx context.Context, dockerCli command.Cli, backend api.Service, opts pauseOptions, services []string) error { + project, name, err := opts.projectOrName(dockerCli, services...) if err != nil { return err } @@ -59,7 +60,7 @@ type unpauseOptions struct { *ProjectOptions } -func unpauseCommand(p *ProjectOptions, backend api.Service) *cobra.Command { +func unpauseCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command { opts := unpauseOptions{ ProjectOptions: p, } @@ -67,15 +68,15 @@ func unpauseCommand(p *ProjectOptions, backend api.Service) *cobra.Command { Use: "unpause [SERVICE...]", Short: "Unpause services", RunE: Adapt(func(ctx context.Context, args []string) error { - return runUnPause(ctx, backend, opts, args) + return runUnPause(ctx, dockerCli, backend, opts, args) }), - ValidArgsFunction: completeServiceNames(p), + ValidArgsFunction: completeServiceNames(dockerCli, p), } return cmd } -func runUnPause(ctx context.Context, backend api.Service, opts unpauseOptions, services []string) error { - project, name, err := opts.projectOrName(services...) +func runUnPause(ctx context.Context, dockerCli command.Cli, backend api.Service, opts unpauseOptions, services []string) error { + project, name, err := opts.projectOrName(dockerCli, services...) if err != nil { return err } diff --git a/cmd/compose/port.go b/cmd/compose/port.go index 11c68724b52..0baa875c3c2 100644 --- a/cmd/compose/port.go +++ b/cmd/compose/port.go @@ -22,6 +22,7 @@ import ( "strconv" "strings" + "github.com/docker/cli/cli/command" "github.com/spf13/cobra" "github.com/docker/compose/v2/pkg/api" @@ -34,7 +35,7 @@ type portOptions struct { index int } -func portCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *cobra.Command { +func portCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command { opts := portOptions{ ProjectOptions: p, } @@ -52,17 +53,17 @@ func portCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *c return nil }), RunE: Adapt(func(ctx context.Context, args []string) error { - return runPort(ctx, streams, backend, opts, args[0]) + return runPort(ctx, dockerCli, backend, opts, args[0]) }), - ValidArgsFunction: completeServiceNames(p), + ValidArgsFunction: completeServiceNames(dockerCli, p), } cmd.Flags().StringVar(&opts.protocol, "protocol", "tcp", "tcp or udp") cmd.Flags().IntVar(&opts.index, "index", 0, "index of the container if service has multiple replicas") return cmd } -func runPort(ctx context.Context, streams api.Streams, backend api.Service, opts portOptions, service string) error { - projectName, err := opts.toProjectName() +func runPort(ctx context.Context, dockerCli command.Cli, backend api.Service, opts portOptions, service string) error { + projectName, err := opts.toProjectName(dockerCli) if err != nil { return err } @@ -74,6 +75,6 @@ func runPort(ctx context.Context, streams api.Streams, backend api.Service, opts return err } - fmt.Fprintf(streams.Out(), "%s:%d\n", ip, port) + fmt.Fprintf(dockerCli.Out(), "%s:%d\n", ip, port) return nil } diff --git a/cmd/compose/ps.go b/cmd/compose/ps.go index 1386234ea15..e1b4487454d 100644 --- a/cmd/compose/ps.go +++ b/cmd/compose/ps.go @@ -75,7 +75,7 @@ func psCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *c RunE: Adapt(func(ctx context.Context, args []string) error { return runPs(ctx, dockerCli, backend, args, opts) }), - ValidArgsFunction: completeServiceNames(p), + ValidArgsFunction: completeServiceNames(dockerCli, p), } flags := psCmd.Flags() flags.StringVar(&opts.Format, "format", "table", cliflags.FormatHelp) @@ -88,7 +88,7 @@ func psCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *c } func runPs(ctx context.Context, dockerCli command.Cli, backend api.Service, services []string, opts psOptions) error { - project, name, err := opts.projectOrName(services...) + project, name, err := opts.projectOrName(dockerCli, services...) if err != nil { return err } diff --git a/cmd/compose/publish.go b/cmd/compose/publish.go index f7c2029661a..244724e8f2f 100644 --- a/cmd/compose/publish.go +++ b/cmd/compose/publish.go @@ -19,12 +19,13 @@ package compose import ( "context" + "github.com/docker/cli/cli/command" "github.com/spf13/cobra" "github.com/docker/compose/v2/pkg/api" ) -func publishCommand(p *ProjectOptions, backend api.Service) *cobra.Command { +func publishCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command { opts := pushOptions{ ProjectOptions: p, } @@ -32,18 +33,18 @@ func publishCommand(p *ProjectOptions, backend api.Service) *cobra.Command { Use: "publish [OPTIONS] [REPOSITORY]", Short: "Publish compose application", RunE: Adapt(func(ctx context.Context, args []string) error { - return runPublish(ctx, backend, opts, args[0]) + return runPublish(ctx, dockerCli, backend, opts, args[0]) }), Args: cobra.ExactArgs(1), } return publishCmd } -func runPublish(ctx context.Context, backend api.Service, opts pushOptions, repository string) error { - project, err := opts.ToProject(nil) +func runPublish(ctx context.Context, dockerCli command.Cli, backend api.Service, opts pushOptions, repository string) error { + project, err := opts.ToProject(dockerCli, nil) if err != nil { return err } - return backend.Publish(ctx, project, repository) + return backend.Publish(ctx, project, repository, api.PublishOptions{}) } diff --git a/cmd/compose/pull.go b/cmd/compose/pull.go index a962beec5c6..5b0af8e12dc 100644 --- a/cmd/compose/pull.go +++ b/cmd/compose/pull.go @@ -22,6 +22,7 @@ import ( "os" "github.com/compose-spec/compose-go/types" + "github.com/docker/cli/cli/command" "github.com/morikuni/aec" "github.com/spf13/cobra" @@ -39,7 +40,7 @@ type pullOptions struct { noBuildable bool } -func pullCommand(p *ProjectOptions, backend api.Service) *cobra.Command { +func pullCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command { opts := pullOptions{ ProjectOptions: p, } @@ -53,9 +54,9 @@ func pullCommand(p *ProjectOptions, backend api.Service) *cobra.Command { return nil }), RunE: Adapt(func(ctx context.Context, args []string) error { - return runPull(ctx, backend, opts, args) + return runPull(ctx, dockerCli, backend, opts, args) }), - ValidArgsFunction: completeServiceNames(p), + ValidArgsFunction: completeServiceNames(dockerCli, p), } flags := cmd.Flags() flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Pull without printing progress information.") @@ -69,8 +70,8 @@ func pullCommand(p *ProjectOptions, backend api.Service) *cobra.Command { return cmd } -func runPull(ctx context.Context, backend api.Service, opts pullOptions, services []string) error { - project, err := opts.ToProject(services) +func runPull(ctx context.Context, dockerCli command.Cli, backend api.Service, opts pullOptions, services []string) error { + project, err := opts.ToProject(dockerCli, services) if err != nil { return err } diff --git a/cmd/compose/push.go b/cmd/compose/push.go index 9e878f96bba..712b22ceacc 100644 --- a/cmd/compose/push.go +++ b/cmd/compose/push.go @@ -20,6 +20,7 @@ import ( "context" "github.com/compose-spec/compose-go/types" + "github.com/docker/cli/cli/command" "github.com/spf13/cobra" "github.com/docker/compose/v2/pkg/api" @@ -33,7 +34,7 @@ type pushOptions struct { Quiet bool } -func pushCommand(p *ProjectOptions, backend api.Service) *cobra.Command { +func pushCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command { opts := pushOptions{ ProjectOptions: p, } @@ -41,9 +42,9 @@ func pushCommand(p *ProjectOptions, backend api.Service) *cobra.Command { Use: "push [OPTIONS] [SERVICE...]", Short: "Push service images", RunE: Adapt(func(ctx context.Context, args []string) error { - return runPush(ctx, backend, opts, args) + return runPush(ctx, dockerCli, backend, opts, args) }), - ValidArgsFunction: completeServiceNames(p), + ValidArgsFunction: completeServiceNames(dockerCli, p), } pushCmd.Flags().BoolVar(&opts.Ignorefailures, "ignore-push-failures", false, "Push what it can and ignores images with push failures") pushCmd.Flags().BoolVar(&opts.IncludeDeps, "include-deps", false, "Also push images of services declared as dependencies") @@ -52,8 +53,8 @@ func pushCommand(p *ProjectOptions, backend api.Service) *cobra.Command { return pushCmd } -func runPush(ctx context.Context, backend api.Service, opts pushOptions, services []string) error { - project, err := opts.ToProject(services) +func runPush(ctx context.Context, dockerCli command.Cli, backend api.Service, opts pushOptions, services []string) error { + project, err := opts.ToProject(dockerCli, services) if err != nil { return err } diff --git a/cmd/compose/remove.go b/cmd/compose/remove.go index 93b0e4f62ec..46ac7b13fe9 100644 --- a/cmd/compose/remove.go +++ b/cmd/compose/remove.go @@ -19,6 +19,7 @@ package compose import ( "context" + "github.com/docker/cli/cli/command" "github.com/docker/compose/v2/pkg/api" "github.com/spf13/cobra" ) @@ -30,7 +31,7 @@ type removeOptions struct { volumes bool } -func removeCommand(p *ProjectOptions, backend api.Service) *cobra.Command { +func removeCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command { opts := removeOptions{ ProjectOptions: p, } @@ -44,9 +45,9 @@ can override this with -v. To list all volumes, use "docker volume ls". Any data which is not in a volume will be lost.`, RunE: Adapt(func(ctx context.Context, args []string) error { - return runRemove(ctx, backend, opts, args) + return runRemove(ctx, dockerCli, backend, opts, args) }), - ValidArgsFunction: completeServiceNames(p), + ValidArgsFunction: completeServiceNames(dockerCli, p), } f := cmd.Flags() f.BoolVarP(&opts.force, "force", "f", false, "Don't ask to confirm removal") @@ -58,8 +59,8 @@ Any data which is not in a volume will be lost.`, return cmd } -func runRemove(ctx context.Context, backend api.Service, opts removeOptions, services []string) error { - project, name, err := opts.projectOrName(services...) +func runRemove(ctx context.Context, dockerCli command.Cli, backend api.Service, opts removeOptions, services []string) error { + project, name, err := opts.projectOrName(dockerCli, services...) if err != nil { return err } diff --git a/cmd/compose/restart.go b/cmd/compose/restart.go index 5c87616fc32..7dad375bc46 100644 --- a/cmd/compose/restart.go +++ b/cmd/compose/restart.go @@ -20,6 +20,7 @@ import ( "context" "time" + "github.com/docker/cli/cli/command" "github.com/spf13/cobra" "github.com/docker/compose/v2/pkg/api" @@ -32,7 +33,7 @@ type restartOptions struct { noDeps bool } -func restartCommand(p *ProjectOptions, backend api.Service) *cobra.Command { +func restartCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command { opts := restartOptions{ ProjectOptions: p, } @@ -43,9 +44,9 @@ func restartCommand(p *ProjectOptions, backend api.Service) *cobra.Command { opts.timeChanged = cmd.Flags().Changed("timeout") }, RunE: Adapt(func(ctx context.Context, args []string) error { - return runRestart(ctx, backend, opts, args) + return runRestart(ctx, dockerCli, backend, opts, args) }), - ValidArgsFunction: completeServiceNames(p), + ValidArgsFunction: completeServiceNames(dockerCli, p), } flags := restartCmd.Flags() flags.IntVarP(&opts.timeout, "timeout", "t", 0, "Specify a shutdown timeout in seconds") @@ -54,8 +55,8 @@ func restartCommand(p *ProjectOptions, backend api.Service) *cobra.Command { return restartCmd } -func runRestart(ctx context.Context, backend api.Service, opts restartOptions, services []string) error { - project, name, err := opts.projectOrName() +func runRestart(ctx context.Context, dockerCli command.Cli, backend api.Service, opts restartOptions, services []string) error { + project, name, err := opts.projectOrName(dockerCli) if err != nil { return err } diff --git a/cmd/compose/run.go b/cmd/compose/run.go index 796de54c6fd..a3004eb63e4 100644 --- a/cmd/compose/run.go +++ b/cmd/compose/run.go @@ -26,6 +26,7 @@ import ( cgo "github.com/compose-spec/compose-go/cli" "github.com/compose-spec/compose-go/loader" "github.com/compose-spec/compose-go/types" + "github.com/docker/cli/cli/command" "github.com/docker/cli/opts" "github.com/mattn/go-shellwords" "github.com/spf13/cobra" @@ -111,7 +112,7 @@ func (options runOptions) apply(project *types.Project) error { return nil } -func runCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *cobra.Command { +func runCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command { options := runOptions{ composeOptions: &composeOptions{ ProjectOptions: p, @@ -152,7 +153,7 @@ func runCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *co return nil }), RunE: Adapt(func(ctx context.Context, args []string) error { - project, err := p.ToProject([]string{options.Service}, cgo.WithResolvedPaths(true), cgo.WithDiscardEnvFile) + project, err := p.ToProject(dockerCli, []string{options.Service}, cgo.WithResolvedPaths(true), cgo.WithDiscardEnvFile) if err != nil { return err } @@ -162,16 +163,16 @@ func runCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *co } options.ignoreOrphans = utils.StringToBool(project.Environment[ComposeIgnoreOrphans]) - return runRun(ctx, backend, project, options, createOpts, buildOpts, streams) + return runRun(ctx, backend, project, options, createOpts, buildOpts, dockerCli) }), - ValidArgsFunction: completeServiceNames(p), + ValidArgsFunction: completeServiceNames(dockerCli, p), } flags := cmd.Flags() flags.BoolVarP(&options.Detach, "detach", "d", false, "Run container in background and print container ID") flags.StringArrayVarP(&options.environment, "env", "e", []string{}, "Set environment variables") flags.StringArrayVarP(&options.labels, "label", "l", []string{}, "Add or override a label") flags.BoolVar(&options.Remove, "rm", false, "Automatically remove the container when it exits") - flags.BoolVarP(&options.noTty, "no-TTY", "T", !streams.Out().IsTerminal(), "Disable pseudo-TTY allocation (default: auto-detected).") + flags.BoolVarP(&options.noTty, "no-TTY", "T", !dockerCli.Out().IsTerminal(), "Disable pseudo-TTY allocation (default: auto-detected).") flags.StringVar(&options.name, "name", "", "Assign a name to the container") flags.StringVarP(&options.user, "user", "u", "", "Run as specified username or uid") flags.StringVarP(&options.workdir, "workdir", "w", "", "Working directory inside the container") @@ -206,7 +207,7 @@ func normalizeRunFlags(f *pflag.FlagSet, name string) pflag.NormalizedName { return pflag.NormalizedName(name) } -func runRun(ctx context.Context, backend api.Service, project *types.Project, options runOptions, createOpts createOptions, buildOpts buildOptions, streams api.Streams) error { +func runRun(ctx context.Context, backend api.Service, project *types.Project, options runOptions, createOpts createOptions, buildOpts buildOptions, dockerCli command.Cli) error { err := options.apply(project) if err != nil { return err @@ -228,7 +229,7 @@ func runRun(ctx context.Context, backend api.Service, project *types.Project, op buildForDeps = &bo } return startDependencies(ctx, backend, *project, buildForDeps, options.Service, options.ignoreOrphans) - }, streams.Err()) + }, dockerCli.Err()) if err != nil { return err } diff --git a/cmd/compose/start.go b/cmd/compose/start.go index 42e1c7fd5f8..682a7890655 100644 --- a/cmd/compose/start.go +++ b/cmd/compose/start.go @@ -19,6 +19,7 @@ package compose import ( "context" + "github.com/docker/cli/cli/command" "github.com/docker/compose/v2/pkg/api" "github.com/spf13/cobra" ) @@ -27,7 +28,7 @@ type startOptions struct { *ProjectOptions } -func startCommand(p *ProjectOptions, backend api.Service) *cobra.Command { +func startCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command { opts := startOptions{ ProjectOptions: p, } @@ -35,15 +36,15 @@ func startCommand(p *ProjectOptions, backend api.Service) *cobra.Command { Use: "start [SERVICE...]", Short: "Start services", RunE: Adapt(func(ctx context.Context, args []string) error { - return runStart(ctx, backend, opts, args) + return runStart(ctx, dockerCli, backend, opts, args) }), - ValidArgsFunction: completeServiceNames(p), + ValidArgsFunction: completeServiceNames(dockerCli, p), } return startCmd } -func runStart(ctx context.Context, backend api.Service, opts startOptions, services []string) error { - project, name, err := opts.projectOrName(services...) +func runStart(ctx context.Context, dockerCli command.Cli, backend api.Service, opts startOptions, services []string) error { + project, name, err := opts.projectOrName(dockerCli, services...) if err != nil { return err } diff --git a/cmd/compose/stop.go b/cmd/compose/stop.go index 12ae59d8f8d..2818ef4f33a 100644 --- a/cmd/compose/stop.go +++ b/cmd/compose/stop.go @@ -20,6 +20,7 @@ import ( "context" "time" + "github.com/docker/cli/cli/command" "github.com/spf13/cobra" "github.com/docker/compose/v2/pkg/api" @@ -31,7 +32,7 @@ type stopOptions struct { timeout int } -func stopCommand(p *ProjectOptions, backend api.Service) *cobra.Command { +func stopCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command { opts := stopOptions{ ProjectOptions: p, } @@ -42,9 +43,9 @@ func stopCommand(p *ProjectOptions, backend api.Service) *cobra.Command { opts.timeChanged = cmd.Flags().Changed("timeout") }, RunE: Adapt(func(ctx context.Context, args []string) error { - return runStop(ctx, backend, opts, args) + return runStop(ctx, dockerCli, backend, opts, args) }), - ValidArgsFunction: completeServiceNames(p), + ValidArgsFunction: completeServiceNames(dockerCli, p), } flags := cmd.Flags() flags.IntVarP(&opts.timeout, "timeout", "t", 0, "Specify a shutdown timeout in seconds") @@ -52,8 +53,8 @@ func stopCommand(p *ProjectOptions, backend api.Service) *cobra.Command { return cmd } -func runStop(ctx context.Context, backend api.Service, opts stopOptions, services []string) error { - project, name, err := opts.projectOrName(services...) +func runStop(ctx context.Context, dockerCli command.Cli, backend api.Service, opts stopOptions, services []string) error { + project, name, err := opts.projectOrName(dockerCli, services...) if err != nil { return err } diff --git a/cmd/compose/top.go b/cmd/compose/top.go index 45d095bfbb5..fb7dd30d414 100644 --- a/cmd/compose/top.go +++ b/cmd/compose/top.go @@ -24,6 +24,7 @@ import ( "strings" "text/tabwriter" + "github.com/docker/cli/cli/command" "github.com/spf13/cobra" "github.com/docker/compose/v2/pkg/api" @@ -33,7 +34,7 @@ type topOptions struct { *ProjectOptions } -func topCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *cobra.Command { +func topCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command { opts := topOptions{ ProjectOptions: p, } @@ -41,15 +42,15 @@ func topCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *co Use: "top [SERVICES...]", Short: "Display the running processes", RunE: Adapt(func(ctx context.Context, args []string) error { - return runTop(ctx, streams, backend, opts, args) + return runTop(ctx, dockerCli, backend, opts, args) }), - ValidArgsFunction: completeServiceNames(p), + ValidArgsFunction: completeServiceNames(dockerCli, p), } return topCmd } -func runTop(ctx context.Context, streams api.Streams, backend api.Service, opts topOptions, services []string) error { - projectName, err := opts.toProjectName() +func runTop(ctx context.Context, dockerCli command.Cli, backend api.Service, opts topOptions, services []string) error { + projectName, err := opts.toProjectName(dockerCli) if err != nil { return err } @@ -63,8 +64,8 @@ func runTop(ctx context.Context, streams api.Streams, backend api.Service, opts }) for _, container := range containers { - fmt.Fprintf(streams.Out(), "%s\n", container.Name) - err := psPrinter(streams.Out(), func(w io.Writer) { + fmt.Fprintf(dockerCli.Out(), "%s\n", container.Name) + err := psPrinter(dockerCli.Out(), func(w io.Writer) { for _, proc := range container.Processes { info := []interface{}{} for _, p := range proc { diff --git a/cmd/compose/up.go b/cmd/compose/up.go index 7daf5b3bebc..ac561fccdf7 100644 --- a/cmd/compose/up.go +++ b/cmd/compose/up.go @@ -26,6 +26,7 @@ import ( xprogress "github.com/docker/buildx/util/progress" "github.com/compose-spec/compose-go/types" + "github.com/docker/cli/cli/command" "github.com/docker/compose/v2/cmd/formatter" "github.com/spf13/cobra" @@ -73,7 +74,7 @@ func (opts upOptions) apply(project *types.Project, services []string) error { return nil } -func upCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *cobra.Command { +func upCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command { up := upOptions{} create := createOptions{} build := buildOptions{ProjectOptions: p} @@ -85,7 +86,7 @@ func upCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *cob create.timeChanged = cmd.Flags().Changed("timeout") return validateFlags(&up, &create) }), - RunE: p.WithServices(func(ctx context.Context, project *types.Project, services []string) error { + RunE: p.WithServices(dockerCli, func(ctx context.Context, project *types.Project, services []string) error { create.ignoreOrphans = utils.StringToBool(project.Environment[ComposeIgnoreOrphans]) if create.ignoreOrphans && create.removeOrphans { return fmt.Errorf("cannot combine %s and --remove-orphans", ComposeIgnoreOrphans) @@ -93,9 +94,9 @@ func upCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *cob if len(up.attach) != 0 && up.attachDependencies { return errors.New("cannot combine --attach and --attach-dependencies") } - return runUp(ctx, streams, backend, create, up, build, project, services) + return runUp(ctx, dockerCli, backend, create, up, build, project, services) }), - ValidArgsFunction: completeServiceNames(p), + ValidArgsFunction: completeServiceNames(dockerCli, p), } flags := upCmd.Flags() flags.BoolVarP(&up.Detach, "detach", "d", false, "Detached mode: Run containers in the background") @@ -153,7 +154,7 @@ func validateFlags(up *upOptions, create *createOptions) error { func runUp( ctx context.Context, - streams api.Streams, + dockerCli command.Cli, backend api.Service, createOptions createOptions, upOptions upOptions, @@ -212,7 +213,7 @@ func runUp( var consumer api.LogConsumer var attach []string if !upOptions.Detach { - consumer = formatter.NewLogConsumer(ctx, streams.Out(), streams.Err(), !upOptions.noColor, !upOptions.noPrefix, upOptions.timestamp) + consumer = formatter.NewLogConsumer(ctx, dockerCli.Out(), dockerCli.Err(), !upOptions.noColor, !upOptions.noPrefix, upOptions.timestamp) var attachSet utils.Set[string] if len(upOptions.attach) != 0 { diff --git a/cmd/compose/version.go b/cmd/compose/version.go index d48e5781fbe..8ca1ed57b08 100644 --- a/cmd/compose/version.go +++ b/cmd/compose/version.go @@ -33,14 +33,14 @@ type versionOptions struct { short bool } -func versionCommand(streams command.Cli) *cobra.Command { +func versionCommand(dockerCli command.Cli) *cobra.Command { opts := versionOptions{} cmd := &cobra.Command{ Use: "version [OPTIONS]", Short: "Show the Docker Compose version information", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, _ []string) error { - runVersion(opts, streams) + runVersion(opts, dockerCli) return nil }, PersistentPreRunE: func(cmd *cobra.Command, args []string) error { @@ -57,14 +57,14 @@ func versionCommand(streams command.Cli) *cobra.Command { return cmd } -func runVersion(opts versionOptions, streams command.Cli) { +func runVersion(opts versionOptions, dockerCli command.Cli) { if opts.short { - fmt.Fprintln(streams.Out(), strings.TrimPrefix(internal.Version, "v")) + fmt.Fprintln(dockerCli.Out(), strings.TrimPrefix(internal.Version, "v")) return } if opts.format == formatter.JSON { - fmt.Fprintf(streams.Out(), "{\"version\":%q}\n", internal.Version) + fmt.Fprintf(dockerCli.Out(), "{\"version\":%q}\n", internal.Version) return } - fmt.Fprintln(streams.Out(), "Docker Compose version", internal.Version) + fmt.Fprintln(dockerCli.Out(), "Docker Compose version", internal.Version) } diff --git a/cmd/compose/viz.go b/cmd/compose/viz.go index 145340b7ac0..2b4d650739e 100644 --- a/cmd/compose/viz.go +++ b/cmd/compose/viz.go @@ -22,6 +22,7 @@ import ( "os" "strings" + "github.com/docker/cli/cli/command" "github.com/docker/compose/v2/pkg/api" "github.com/spf13/cobra" ) @@ -34,7 +35,7 @@ type vizOptions struct { indentationStr string } -func vizCommand(p *ProjectOptions, backend api.Service) *cobra.Command { +func vizCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command { opts := vizOptions{ ProjectOptions: p, } @@ -50,7 +51,7 @@ func vizCommand(p *ProjectOptions, backend api.Service) *cobra.Command { return err }), RunE: Adapt(func(ctx context.Context, args []string) error { - return runViz(ctx, backend, &opts) + return runViz(ctx, dockerCli, backend, &opts) }), } @@ -62,9 +63,9 @@ func vizCommand(p *ProjectOptions, backend api.Service) *cobra.Command { return cmd } -func runViz(ctx context.Context, backend api.Service, opts *vizOptions) error { +func runViz(ctx context.Context, dockerCli command.Cli, backend api.Service, opts *vizOptions) error { _, _ = fmt.Fprintln(os.Stderr, "viz command is EXPERIMENTAL") - project, err := opts.ToProject(nil) + project, err := opts.ToProject(dockerCli, nil) if err != nil { return err } diff --git a/cmd/compose/wait.go b/cmd/compose/wait.go index 6570b2d2524..e95818b9592 100644 --- a/cmd/compose/wait.go +++ b/cmd/compose/wait.go @@ -21,6 +21,7 @@ import ( "os" "github.com/docker/cli/cli" + "github.com/docker/cli/cli/command" "github.com/docker/compose/v2/pkg/api" "github.com/spf13/cobra" ) @@ -33,7 +34,7 @@ type waitOptions struct { downProject bool } -func waitCommand(p *ProjectOptions, backend api.Service) *cobra.Command { +func waitCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command { opts := waitOptions{ ProjectOptions: p, } @@ -46,7 +47,7 @@ func waitCommand(p *ProjectOptions, backend api.Service) *cobra.Command { Args: cli.RequiresMinArgs(1), RunE: Adapt(func(ctx context.Context, services []string) error { opts.services = services - statusCode, err = runWait(ctx, backend, &opts) + statusCode, err = runWait(ctx, dockerCli, backend, &opts) return err }), PostRun: func(cmd *cobra.Command, args []string) { @@ -59,8 +60,8 @@ func waitCommand(p *ProjectOptions, backend api.Service) *cobra.Command { return cmd } -func runWait(ctx context.Context, backend api.Service, opts *waitOptions) (int64, error) { - _, name, err := opts.projectOrName() +func runWait(ctx context.Context, dockerCli command.Cli, backend api.Service, opts *waitOptions) (int64, error) { + _, name, err := opts.projectOrName(dockerCli) if err != nil { return 0, err } diff --git a/cmd/compose/watch.go b/cmd/compose/watch.go index 5c3a26a7a41..4003470ad25 100644 --- a/cmd/compose/watch.go +++ b/cmd/compose/watch.go @@ -21,6 +21,7 @@ import ( "fmt" "os" + "github.com/docker/cli/cli/command" "github.com/docker/compose/v2/internal/locker" "github.com/docker/compose/v2/pkg/api" @@ -32,7 +33,7 @@ type watchOptions struct { quiet bool } -func watchCommand(p *ProjectOptions, backend api.Service) *cobra.Command { +func watchCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command { opts := watchOptions{ ProjectOptions: p, } @@ -43,18 +44,18 @@ func watchCommand(p *ProjectOptions, backend api.Service) *cobra.Command { return nil }), RunE: Adapt(func(ctx context.Context, args []string) error { - return runWatch(ctx, backend, opts, args) + return runWatch(ctx, dockerCli, backend, opts, args) }), - ValidArgsFunction: completeServiceNames(p), + ValidArgsFunction: completeServiceNames(dockerCli, p), } cmd.Flags().BoolVar(&opts.quiet, "quiet", false, "hide build output") return cmd } -func runWatch(ctx context.Context, backend api.Service, opts watchOptions, services []string) error { +func runWatch(ctx context.Context, dockerCli command.Cli, backend api.Service, opts watchOptions, services []string) error { fmt.Fprintln(os.Stderr, "watch command is EXPERIMENTAL") - project, err := opts.ToProject(nil) + project, err := opts.ToProject(dockerCli, nil) if err != nil { return err } diff --git a/go.mod b/go.mod index f38fc2551ac..69f8c51bc64 100644 --- a/go.mod +++ b/go.mod @@ -54,6 +54,7 @@ require ( github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Masterminds/semver/v3 v3.2.1 // indirect + github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d // indirect github.com/aws/aws-sdk-go-v2 v1.17.6 // indirect github.com/aws/aws-sdk-go-v2/config v1.18.16 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.13.16 // indirect diff --git a/go.sum b/go.sum index 15a85e03d78..25113c16af4 100644 --- a/go.sum +++ b/go.sum @@ -60,8 +60,9 @@ github.com/Microsoft/hcsshim v0.10.0-rc.8/go.mod h1:OEthFdQv/AD2RAdzR6Mm1N1KPCzt github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/Shopify/logrus-bugsnag v0.0.0-20170309145241-6dbc35f2c30d h1:hi6J4K6DKrR4/ljxn6SF6nURyu785wKMuQcjt7H3VCQ= github.com/Shopify/logrus-bugsnag v0.0.0-20170309145241-6dbc35f2c30d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= +github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= +github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls= github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= diff --git a/pkg/api/api.go b/pkg/api/api.go index 3c2a4ef76d7..f375760480c 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -75,7 +75,7 @@ type Service interface { // Port executes the equivalent to a `compose port` Port(ctx context.Context, projectName string, service string, port uint16, options PortOptions) (string, int, error) // Publish executes the equivalent to a `compose publish` - Publish(ctx context.Context, project *types.Project, repository string) error + Publish(ctx context.Context, project *types.Project, repository string, options PublishOptions) error // Images executes the equivalent of a `compose images` Images(ctx context.Context, projectName string, options ImagesOptions) ([]ImageSummary, error) // MaxConcurrency defines upper limit for concurrent operations against engine API @@ -354,6 +354,10 @@ type PortOptions struct { Index int } +// PublishOptions group options of the Publish API +type PublishOptions struct { +} + func (e Event) String() string { t := e.Timestamp.Format("2006-01-02 15:04:05.000000") var attr []string diff --git a/pkg/api/proxy.go b/pkg/api/proxy.go index b74fe098f20..7cb40b0030e 100644 --- a/pkg/api/proxy.go +++ b/pkg/api/proxy.go @@ -55,7 +55,7 @@ type ServiceProxy struct { DryRunModeFn func(ctx context.Context, dryRun bool) (context.Context, error) VizFn func(ctx context.Context, project *types.Project, options VizOptions) (string, error) WaitFn func(ctx context.Context, projectName string, options WaitOptions) (int64, error) - PublishFn func(ctx context.Context, project *types.Project, repository string) error + PublishFn func(ctx context.Context, project *types.Project, repository string, options PublishOptions) error interceptors []Interceptor } @@ -313,8 +313,8 @@ func (s *ServiceProxy) Port(ctx context.Context, projectName string, service str return s.PortFn(ctx, projectName, service, port, options) } -func (s *ServiceProxy) Publish(ctx context.Context, project *types.Project, repository string) error { - return s.PublishFn(ctx, project, repository) +func (s *ServiceProxy) Publish(ctx context.Context, project *types.Project, repository string, options PublishOptions) error { + return s.PublishFn(ctx, project, repository, options) } // Images implements Service interface diff --git a/pkg/compose/publish.go b/pkg/compose/publish.go index 97d7aa61495..5f83b9b72a9 100644 --- a/pkg/compose/publish.go +++ b/pkg/compose/publish.go @@ -20,11 +20,11 @@ import ( "context" "github.com/compose-spec/compose-go/types" - "github.com/distribution/distribution/v3/reference" + "github.com/distribution/reference" "github.com/docker/compose/v2/pkg/api" ) -func (s *composeService) Publish(ctx context.Context, project *types.Project, repository string) error { +func (s *composeService) Publish(ctx context.Context, project *types.Project, repository string, options api.PublishOptions) error { err := s.Push(ctx, project, api.PushOptions{}) if err != nil { return err diff --git a/pkg/mocks/mock_docker_compose_api.go b/pkg/mocks/mock_docker_compose_api.go index 428d2665a23..12a39a72947 100644 --- a/pkg/mocks/mock_docker_compose_api.go +++ b/pkg/mocks/mock_docker_compose_api.go @@ -267,17 +267,17 @@ func (mr *MockServiceMockRecorder) Ps(ctx, projectName, options interface{}) *go } // Publish mocks base method. -func (m *MockService) Publish(ctx context.Context, project *types.Project, repository string) error { +func (m *MockService) Publish(ctx context.Context, project *types.Project, repository string, options api.PublishOptions) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Publish", ctx, project, repository) + ret := m.ctrl.Call(m, "Publish", ctx, project, repository, options) ret0, _ := ret[0].(error) return ret0 } // Publish indicates an expected call of Publish. -func (mr *MockServiceMockRecorder) Publish(ctx, project, repository interface{}) *gomock.Call { +func (mr *MockServiceMockRecorder) Publish(ctx, project, repository, options interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Publish", reflect.TypeOf((*MockService)(nil).Publish), ctx, project, repository) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Publish", reflect.TypeOf((*MockService)(nil).Publish), ctx, project, repository, options) } // Pull mocks base method. diff --git a/pkg/remote/git.go b/pkg/remote/git.go index 84dd101addf..e0ab25cc34e 100644 --- a/pkg/remote/git.go +++ b/pkg/remote/git.go @@ -46,7 +46,7 @@ func GitRemoteLoaderEnabled() (bool, error) { return false, nil } -func NewGitRemoteLoader() (loader.ResourceLoader, error) { +func NewGitRemoteLoader(offline bool) (loader.ResourceLoader, error) { // xdg.CacheFile creates the parent directories for the target file path // and returns the fully qualified path, so use "git" as a filename and // then chop it off after, i.e. no ~/.cache/docker-compose/git file will @@ -57,12 +57,14 @@ func NewGitRemoteLoader() (loader.ResourceLoader, error) { } cache = filepath.Dir(cache) return gitRemoteLoader{ - cache: cache, + cache: cache, + offline: offline, }, err } type gitRemoteLoader struct { - cache string + cache string + offline bool } func (g gitRemoteLoader) Accept(path string) bool { @@ -104,6 +106,9 @@ func (g gitRemoteLoader) Load(ctx context.Context, path string) (string, error) local := filepath.Join(g.cache, ref.Commit) if _, err := os.Stat(local); os.IsNotExist(err) { + if g.offline { + return "", nil + } err = g.checkout(ctx, local, ref) if err != nil { return "", err @@ -167,7 +172,7 @@ func (g gitRemoteLoader) gitCommandEnv() []string { // Disable any ssh connection pooling by Git and do not attempt to prompt the user. env["GIT_SSH_COMMAND"] = "ssh -o ControlMaster=no -o BatchMode=yes" } - v := values(env) + v := env.Values() return v } @@ -182,11 +187,3 @@ func findFile(names []string, pwd string) (string, error) { } var _ loader.ResourceLoader = gitRemoteLoader{} - -func values(m types.Mapping) []string { - values := make([]string, 0, len(m)) - for k, v := range m { - values = append(values, fmt.Sprintf("%s=%s", k, v)) - } - return values -} diff --git a/pkg/remote/oci.go b/pkg/remote/oci.go new file mode 100644 index 00000000000..2e87b928289 --- /dev/null +++ b/pkg/remote/oci.go @@ -0,0 +1,145 @@ +/* + Copyright 2020 Docker Compose CLI authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package remote + +import ( + "context" + "encoding/json" + "fmt" + "os" + "path/filepath" + "strconv" + "strings" + + "github.com/adrg/xdg" + "github.com/distribution/reference" + "github.com/docker/buildx/store/storeutil" + "github.com/docker/buildx/util/imagetools" + "github.com/docker/cli/cli/command" + v1 "github.com/opencontainers/image-spec/specs-go/v1" + + "github.com/compose-spec/compose-go/loader" + "github.com/pkg/errors" +) + +func OCIRemoteLoaderEnabled() (bool, error) { + if v := os.Getenv("COMPOSE_EXPERIMENTAL_OCI_REMOTE"); v != "" { + enabled, err := strconv.ParseBool(v) + if err != nil { + return false, errors.Wrap(err, "COMPOSE_EXPERIMENTAL_OCI_REMOTE environment variable expects boolean value") + } + return enabled, err + } + return false, nil +} + +func NewOCIRemoteLoader(dockerCli command.Cli, offline bool) (loader.ResourceLoader, error) { + // xdg.CacheFile creates the parent directories for the target file path + // and returns the fully qualified path, so use "git" as a filename and + // then chop it off after, i.e. no ~/.cache/docker-compose/git file will + // ever be created + cache, err := xdg.CacheFile(filepath.Join("docker-compose", "oci")) + if err != nil { + return nil, fmt.Errorf("initializing git cache: %w", err) + } + cache = filepath.Dir(cache) + return ociRemoteLoader{ + cache: cache, + dockerCli: dockerCli, + offline: offline, + }, err +} + +type ociRemoteLoader struct { + cache string + dockerCli command.Cli + offline bool +} + +const prefix = "oci:" + +func (g ociRemoteLoader) Accept(path string) bool { + return strings.HasPrefix(path, prefix) +} + +func (g ociRemoteLoader) Load(ctx context.Context, path string) (string, error) { + if g.offline { + return "", nil + } + + ref, err := reference.ParseDockerRef(path[len(prefix):]) + if err != nil { + return "", err + } + + opt, err := storeutil.GetImageConfig(g.dockerCli, nil) + if err != nil { + return "", err + } + resolver := imagetools.New(opt) + + content, descriptor, err := resolver.Get(ctx, ref.String()) + if err != nil { + return "", err + } + descriptor.Digest.Hex() + + local := filepath.Join(g.cache) + composeFile := filepath.Join(local, "compose.yaml") + if _, err = os.Stat(local); os.IsNotExist(err) { + + err = os.MkdirAll(local, 0o700) + if err != nil { + return "", err + } + + f, err := os.Create(composeFile) + if err != nil { + return "", err + } + defer f.Close() //nolint:errcheck + + var descriptor v1.Manifest + err = json.Unmarshal(content, &descriptor) + if err != nil { + return "", err + } + for i, layer := range descriptor.Layers { + digested, err := reference.WithDigest(ref, layer.Digest) + if err != nil { + return "", err + } + content, _, err := resolver.Get(ctx, digested.String()) + if err != nil { + return "", err + } + if i > 0 { + _, err = f.Write([]byte("\n---\n")) + if err != nil { + return "", err + } + } + _, err = f.Write(content) + if err != nil { + return "", err + } + } + } + return composeFile, nil +} + +var _ loader.ResourceLoader = ociRemoteLoader{}