From c74d06c028236eb86d5567a3a41fdcd2abaa91d5 Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Wed, 30 Aug 2023 10:15:35 +0200 Subject: [PATCH] pull OCI remote resource Signed-off-by: Nicolas De Loof --- cmd/compose/alpha.go | 9 +-- cmd/compose/build.go | 11 ++-- cmd/compose/completion.go | 9 +-- cmd/compose/compose.go | 95 +++++++++++++++------------ cmd/compose/config.go | 66 +++++++++---------- cmd/compose/cp.go | 11 ++-- cmd/compose/create.go | 7 +- cmd/compose/down.go | 9 +-- cmd/compose/events.go | 15 +++-- cmd/compose/exec.go | 13 ++-- cmd/compose/images.go | 15 +++-- cmd/compose/kill.go | 11 ++-- cmd/compose/list.go | 11 ++-- cmd/compose/logs.go | 13 ++-- cmd/compose/pause.go | 21 +++--- cmd/compose/port.go | 13 ++-- cmd/compose/ps.go | 4 +- cmd/compose/publish.go | 9 +-- cmd/compose/pull.go | 11 ++-- cmd/compose/push.go | 11 ++-- cmd/compose/remove.go | 11 ++-- cmd/compose/restart.go | 11 ++-- cmd/compose/run.go | 17 ++--- cmd/compose/start.go | 11 ++-- cmd/compose/stop.go | 11 ++-- cmd/compose/top.go | 15 +++-- cmd/compose/up.go | 13 ++-- cmd/compose/version.go | 12 ++-- cmd/compose/viz.go | 9 +-- cmd/compose/wait.go | 9 +-- cmd/compose/watch.go | 11 ++-- pkg/remote/git.go | 10 +-- pkg/remote/oci.go | 135 ++++++++++++++++++++++++++++++++++++++ 33 files changed, 399 insertions(+), 240 deletions(-) create mode 100644 pkg/remote/oci.go 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 d8993a26c79..7a87e64799f 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" @@ -73,7 +74,7 @@ func (opts buildOptions) toAPIBuildOptions(services []string) (api.BuildOptions, }, nil } -func buildCommand(p *ProjectOptions, progress *string, backend api.Service) *cobra.Command { +func buildCommand(p *ProjectOptions, dockerCli command.Cli, progress *string, backend api.Service) *cobra.Command { opts := buildOptions{ ProjectOptions: p, } @@ -98,9 +99,9 @@ func buildCommand(p *ProjectOptions, progress *string, backend api.Service) *cob 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") @@ -124,8 +125,8 @@ func buildCommand(p *ProjectOptions, progress *string, backend api.Service) *cob 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..ca00f28163b 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,9 @@ 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) + project, err := p.ToProject(dockerCli, nil) if err != nil { return nil, cobra.ShellCompDirectiveNoFileComp } @@ -67,9 +68,9 @@ 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) + 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 29c6f90e7a7..d14ebbb5a2a 100644 --- a/cmd/compose/compose.go +++ b/cmd/compose/compose.go @@ -26,17 +26,16 @@ import ( "strings" "syscall" - "github.com/compose-spec/compose-go/dotenv" - buildx "github.com/docker/buildx/util/progress" - "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" @@ -126,14 +125,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), @@ -141,19 +140,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 } @@ -173,11 +160,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 != "" { @@ -191,7 +178,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 } @@ -201,14 +188,38 @@ 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) { + enabled, err := remote.GitRemoteLoaderEnabled() + if err != nil { + return nil, err + } + if enabled { + git, err := remote.NewGitRemoteLoader() + 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) + if err != nil { + return nil, err + } + po = append(po, cli.WithResourceLoader(git)) + } + options, err := o.toProjectOptions(po...) if err != nil { return nil, compose.WrapComposeError(err) @@ -427,32 +438,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, &progress, backend), - pushCommand(&opts, backend), - pullCommand(&opts, backend), - createCommand(&opts, backend), - copyCommand(&opts, backend), - waitCommand(&opts, backend), - alphaCommand(&opts, backend), + buildCommand(&opts, dockerCli, &progress, 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) @@ -475,7 +486,7 @@ func RootCommand(dockerCli command.Cli, backend api.Service) *cobra.Command { // ) c.RegisterFlagCompletionFunc( //nolint:errcheck "profile", - completeProfileNames(&opts), + completeProfileNames(dockerCli, &opts), ) c.Flags().StringVar(&progress, "progress", buildx.PrinterModeAuto, fmt.Sprintf(`Set type of progress output (%s)`, strings.Join(printerModes, ", "))) diff --git a/cmd/compose/config.go b/cmd/compose/config.go index 1383558c3e5..5bb81e35439 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.WithProfiles(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 b513c6d93b5..697e83dca44 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..d51e6885340 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,15 +33,15 @@ 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 } 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 90848cd44e0..e47706521f6 100644 --- a/cmd/compose/restart.go +++ b/cmd/compose/restart.go @@ -21,6 +21,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" @@ -33,7 +34,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, } @@ -44,9 +45,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") @@ -55,8 +56,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 ada590ccd79..0cd358fc33e 100644 --- a/cmd/compose/run.go +++ b/cmd/compose/run.go @@ -24,6 +24,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" @@ -109,7 +110,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, @@ -147,22 +148,22 @@ 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 } options.ignoreOrphans = utils.StringToBool(project.Environment[ComposeIgnoreOrphans]) - return runRun(ctx, backend, project, options, createOpts, streams) + return runRun(ctx, backend, project, options, createOpts, 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") @@ -197,7 +198,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, streams api.Streams) error { +func runRun(ctx context.Context, backend api.Service, project *types.Project, options runOptions, createOpts createOptions, dockerCli command.Cli) error { err := options.apply(project) if err != nil { return err @@ -210,7 +211,7 @@ func runRun(ctx context.Context, backend api.Service, project *types.Project, op err = progress.Run(ctx, func(ctx context.Context) error { return startDependencies(ctx, backend, *project, options.Service, options.ignoreOrphans) - }, streams.Err()) + }, dockerCli.Err()) if err != nil { return err } @@ -224,7 +225,7 @@ func runRun(ctx context.Context, backend api.Service, project *types.Project, op labels[parts[0]] = parts[1] } - // start container and attach to container streams + // start container and attach to container dockerCli runOpts := api.RunOptions{ Name: options.name, Service: options.Service, 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 0f49fa7f7c3..7de2c2e88be 100644 --- a/cmd/compose/up.go +++ b/cmd/compose/up.go @@ -24,6 +24,7 @@ import ( "time" "github.com/compose-spec/compose-go/types" + "github.com/docker/cli/cli/command" "github.com/docker/compose/v2/cmd/formatter" "github.com/spf13/cobra" @@ -71,7 +72,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{} upCmd := &cobra.Command{ @@ -82,7 +83,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) @@ -90,9 +91,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, project, services) + return runUp(ctx, dockerCli, backend, create, up, 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") @@ -148,7 +149,7 @@ func validateFlags(up *upOptions, create *createOptions) error { return nil } -func runUp(ctx context.Context, streams api.Streams, backend api.Service, createOptions createOptions, upOptions upOptions, project *types.Project, services []string) error { +func runUp(ctx context.Context, dockerCli command.Cli, backend api.Service, createOptions createOptions, upOptions upOptions, project *types.Project, services []string) error { if len(project.Services) == 0 { return fmt.Errorf("no service selected") } @@ -181,7 +182,7 @@ func runUp(ctx context.Context, streams api.Streams, backend api.Service, create 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/pkg/remote/git.go b/pkg/remote/git.go index 84dd101addf..2bfc08a7783 100644 --- a/pkg/remote/git.go +++ b/pkg/remote/git.go @@ -167,7 +167,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 +182,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..06138901240 --- /dev/null +++ b/pkg/remote/oci.go @@ -0,0 +1,135 @@ +/* + 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/distribution/v3/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) (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, + }, err +} + +type ociRemoteLoader struct { + cache string + dockerCli command.Cli +} + +func (g ociRemoteLoader) Accept(path string) bool { + return strings.HasPrefix(path, "docker:") +} + +func (g ociRemoteLoader) Load(ctx context.Context, path string) (string, error) { + ref, err := reference.ParseDockerRef(path[7:]) + 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 + } + + local := filepath.Join(g.cache, descriptor.Digest.Hex()) + 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{}