Skip to content

Commit bf0a5ac

Browse files
committed
Migrate CLI commands to use LoadProject API
Simplifying the codebase and eliminating duplicate backend creation. Signed-off-by: Guillaume Lours <[email protected]>
1 parent bf49e7b commit bf0a5ac

File tree

12 files changed

+169
-114
lines changed

12 files changed

+169
-114
lines changed

cmd/compose/bridge.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030

3131
"github.com/docker/compose/v2/cmd/formatter"
3232
"github.com/docker/compose/v2/pkg/bridge"
33+
"github.com/docker/compose/v2/pkg/compose"
3334
)
3435

3536
func bridgeCommand(p *ProjectOptions, dockerCli command.Cli) *cobra.Command {
@@ -62,7 +63,12 @@ func convertCommand(p *ProjectOptions, dockerCli command.Cli) *cobra.Command {
6263
}
6364

6465
func runConvert(ctx context.Context, dockerCli command.Cli, p *ProjectOptions, opts bridge.ConvertOptions) error {
65-
project, _, err := p.ToProject(ctx, dockerCli, nil)
66+
backend, err := compose.NewComposeService(dockerCli)
67+
if err != nil {
68+
return err
69+
}
70+
71+
project, _, err := p.ToProject(ctx, dockerCli, backend, nil)
6672
if err != nil {
6773
return err
6874
}

cmd/compose/build.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,13 @@ func buildCommand(p *ProjectOptions, dockerCli command.Cli, backendOptions *Back
150150
}
151151

152152
func runBuild(ctx context.Context, dockerCli command.Cli, backendOptions *BackendOptions, opts buildOptions, services []string) error {
153+
backend, err := compose.NewComposeService(dockerCli, backendOptions.Options...)
154+
if err != nil {
155+
return err
156+
}
157+
153158
opts.All = true // do not drop resources as build may involve some dependencies by additional_contexts
154-
project, _, err := opts.ToProject(ctx, dockerCli, nil, cli.WithResolvedPaths(true), cli.WithoutEnvironmentResolution)
159+
project, _, err := opts.ToProject(ctx, dockerCli, backend, nil, cli.WithoutEnvironmentResolution)
155160
if err != nil {
156161
return err
157162
}
@@ -166,9 +171,5 @@ func runBuild(ctx context.Context, dockerCli command.Cli, backendOptions *Backen
166171
}
167172
apiBuildOptions.Attestations = true
168173

169-
backend, err := compose.NewComposeService(dockerCli, backendOptions.Options...)
170-
if err != nil {
171-
return err
172-
}
173174
return backend.Build(ctx, project, apiBuildOptions)
174175
}

cmd/compose/completion.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,12 @@ func noCompletion() validArgsFn {
3838
func completeServiceNames(dockerCli command.Cli, p *ProjectOptions) validArgsFn {
3939
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
4040
p.Offline = true
41-
project, _, err := p.ToProject(cmd.Context(), dockerCli, nil)
41+
backend, err := compose.NewComposeService(dockerCli)
42+
if err != nil {
43+
return nil, cobra.ShellCompDirectiveNoFileComp
44+
}
45+
46+
project, _, err := p.ToProject(cmd.Context(), dockerCli, backend, nil)
4247
if err != nil {
4348
return nil, cobra.ShellCompDirectiveNoFileComp
4449
}
@@ -79,7 +84,12 @@ func completeProjectNames(dockerCli command.Cli, backendOptions *BackendOptions)
7984
func completeProfileNames(dockerCli command.Cli, p *ProjectOptions) validArgsFn {
8085
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
8186
p.Offline = true
82-
project, _, err := p.ToProject(cmd.Context(), dockerCli, nil)
87+
backend, err := compose.NewComposeService(dockerCli)
88+
if err != nil {
89+
return nil, cobra.ShellCompDirectiveNoFileComp
90+
}
91+
92+
project, _, err := p.ToProject(cmd.Context(), dockerCli, backend, nil)
8393
if err != nil {
8494
return nil, cobra.ShellCompDirectiveNoFileComp
8595
}

cmd/compose/compose.go

Lines changed: 35 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -159,12 +159,12 @@ func (o *ProjectOptions) WithProject(fn ProjectFunc, dockerCli command.Cli) func
159159
// WithServices creates a cobra run command from a ProjectFunc based on configured project options and selected services
160160
func (o *ProjectOptions) WithServices(dockerCli command.Cli, fn ProjectServicesFunc) func(cmd *cobra.Command, args []string) error {
161161
return Adapt(func(ctx context.Context, args []string) error {
162-
options := []cli.ProjectOptionsFn{
163-
cli.WithResolvedPaths(true),
164-
cli.WithoutEnvironmentResolution,
162+
backend, err := compose.NewComposeService(dockerCli)
163+
if err != nil {
164+
return err
165165
}
166166

167-
project, metrics, err := o.ToProject(ctx, dockerCli, args, options...)
167+
project, metrics, err := o.ToProject(ctx, dockerCli, backend, args, cli.WithoutEnvironmentResolution)
168168
if err != nil {
169169
return err
170170
}
@@ -236,7 +236,12 @@ func (o *ProjectOptions) projectOrName(ctx context.Context, dockerCli command.Cl
236236
name := o.ProjectName
237237
var project *types.Project
238238
if len(o.ConfigPaths) > 0 || o.ProjectName == "" {
239-
p, _, err := o.ToProject(ctx, dockerCli, services, cli.WithDiscardEnvFile, cli.WithoutEnvironmentResolution)
239+
backend, err := compose.NewComposeService(dockerCli)
240+
if err != nil {
241+
return nil, "", err
242+
}
243+
244+
p, _, err := o.ToProject(ctx, dockerCli, backend, services, cli.WithDiscardEnvFile, cli.WithoutEnvironmentResolution)
240245
if err != nil {
241246
envProjectName := os.Getenv(ComposeProjectName)
242247
if envProjectName != "" {
@@ -260,7 +265,12 @@ func (o *ProjectOptions) toProjectName(ctx context.Context, dockerCli command.Cl
260265
return envProjectName, nil
261266
}
262267

263-
project, _, err := o.ToProject(ctx, dockerCli, nil)
268+
backend, err := compose.NewComposeService(dockerCli)
269+
if err != nil {
270+
return "", err
271+
}
272+
273+
project, _, err := o.ToProject(ctx, dockerCli, backend, nil)
264274
if err != nil {
265275
return "", err
266276
}
@@ -285,19 +295,14 @@ func (o *ProjectOptions) ToModel(ctx context.Context, dockerCli command.Cli, ser
285295
return options.LoadModel(ctx)
286296
}
287297

288-
func (o *ProjectOptions) ToProject(ctx context.Context, dockerCli command.Cli, services []string, po ...cli.ProjectOptionsFn) (*types.Project, tracing.Metrics, error) { //nolint:gocyclo
298+
// ToProject loads a Compose project using the LoadProject API.
299+
// Accepts optional cli.ProjectOptionsFn to control loader behavior.
300+
func (o *ProjectOptions) ToProject(ctx context.Context, dockerCli command.Cli, backend api.Compose, services []string, po ...cli.ProjectOptionsFn) (*types.Project, tracing.Metrics, error) {
289301
var metrics tracing.Metrics
290302
remotes := o.remoteLoaders(dockerCli)
291-
for _, r := range remotes {
292-
po = append(po, cli.WithResourceLoader(r))
293-
}
294-
295-
options, err := o.toProjectOptions(po...)
296-
if err != nil {
297-
return nil, metrics, err
298-
}
299303

300-
options.WithListeners(func(event string, metadata map[string]any) {
304+
// Setup metrics listener to collect project data
305+
metricsListener := func(event string, metadata map[string]any) {
301306
switch event {
302307
case "extends":
303308
metrics.CountExtends++
@@ -318,50 +323,28 @@ func (o *ProjectOptions) ToProject(ctx context.Context, dockerCli command.Cli, s
318323
}
319324
}
320325
}
321-
})
322-
323-
if o.Compatibility || utils.StringToBool(options.Environment[ComposeCompatibility]) {
324-
api.Separator = "_"
325-
}
326-
327-
project, err := options.LoadProject(ctx)
328-
if err != nil {
329-
return nil, metrics, err
330326
}
331327

332-
if project.Name == "" {
333-
return nil, metrics, errors.New("project name can't be empty. Use `--project-name` to set a valid name")
328+
loadOpts := api.ProjectLoadOptions{
329+
ProjectName: o.ProjectName,
330+
ConfigPaths: o.ConfigPaths,
331+
WorkingDir: o.ProjectDir,
332+
EnvFiles: o.EnvFiles,
333+
Profiles: o.Profiles,
334+
Services: services,
335+
Offline: o.Offline,
336+
All: o.All,
337+
Compatibility: o.Compatibility,
338+
ProjectOptionsFns: po,
339+
LoadListeners: []api.LoadListener{metricsListener},
334340
}
335341

336-
project, err = project.WithServicesEnabled(services...)
342+
project, err := backend.LoadProject(ctx, loadOpts)
337343
if err != nil {
338344
return nil, metrics, err
339345
}
340346

341-
for name, s := range project.Services {
342-
s.CustomLabels = map[string]string{
343-
api.ProjectLabel: project.Name,
344-
api.ServiceLabel: name,
345-
api.VersionLabel: api.ComposeVersion,
346-
api.WorkingDirLabel: project.WorkingDir,
347-
api.ConfigFilesLabel: strings.Join(project.ComposeFiles, ","),
348-
api.OneoffLabel: "False", // default, will be overridden by `run` command
349-
}
350-
if len(o.EnvFiles) != 0 {
351-
s.CustomLabels[api.EnvironmentFileLabel] = strings.Join(o.EnvFiles, ",")
352-
}
353-
project.Services[name] = s
354-
}
355-
356-
project, err = project.WithSelectedServices(services)
357-
if err != nil {
358-
return nil, tracing.Metrics{}, err
359-
}
360-
361-
if !o.All {
362-
project = project.WithoutUnnecessaryResources()
363-
}
364-
return project, metrics, err
347+
return project, metrics, nil
365348
}
366349

367350
func (o *ProjectOptions) remoteLoaders(dockerCli command.Cli) []loader.ResourceLoader {

cmd/compose/config.go

Lines changed: 66 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -61,26 +61,30 @@ type configOptions struct {
6161
lockImageDigests bool
6262
}
6363

64-
func (o *configOptions) ToProject(ctx context.Context, dockerCli command.Cli, services []string, po ...cli.ProjectOptionsFn) (*types.Project, error) {
65-
po = append(po, o.ToProjectOptions()...)
66-
project, _, err := o.ProjectOptions.ToProject(ctx, dockerCli, services, po...)
64+
func (o *configOptions) ToProject(ctx context.Context, dockerCli command.Cli, backend api.Compose, services []string) (*types.Project, error) {
65+
project, _, err := o.ProjectOptions.ToProject(ctx, dockerCli, backend, services, o.toProjectOptionsFns()...)
6766
return project, err
6867
}
6968

7069
func (o *configOptions) ToModel(ctx context.Context, dockerCli command.Cli, services []string, po ...cli.ProjectOptionsFn) (map[string]any, error) {
71-
po = append(po, o.ToProjectOptions()...)
70+
po = append(po, o.toProjectOptionsFns()...)
7271
return o.ProjectOptions.ToModel(ctx, dockerCli, services, po...)
7372
}
7473

75-
func (o *configOptions) ToProjectOptions() []cli.ProjectOptionsFn {
76-
return []cli.ProjectOptionsFn{
74+
// toProjectOptionsFns converts config options to cli.ProjectOptionsFn
75+
func (o *configOptions) toProjectOptionsFns() []cli.ProjectOptionsFn {
76+
fns := []cli.ProjectOptionsFn{
7777
cli.WithInterpolation(!o.noInterpolate),
7878
cli.WithResolvedPaths(!o.noResolvePath),
7979
cli.WithNormalization(!o.noNormalize),
8080
cli.WithConsistency(!o.noConsistency),
8181
cli.WithDefaultProfiles(o.Profiles...),
8282
cli.WithDiscardEnvFile,
8383
}
84+
if o.noResolveEnv {
85+
fns = append(fns, cli.WithoutEnvironmentResolution)
86+
}
87+
return fns
8488
}
8589

8690
func configCommand(p *ProjectOptions, dockerCli command.Cli) *cobra.Command {
@@ -197,7 +201,12 @@ func runConfig(ctx context.Context, dockerCli command.Cli, opts configOptions, s
197201
}
198202

199203
func runConfigInterpolate(ctx context.Context, dockerCli command.Cli, opts configOptions, services []string) ([]byte, error) {
200-
project, err := opts.ToProject(ctx, dockerCli, services)
204+
backend, err := compose.NewComposeService(dockerCli)
205+
if err != nil {
206+
return nil, err
207+
}
208+
209+
project, err := opts.ToProject(ctx, dockerCli, backend, services)
201210
if err != nil {
202211
return nil, err
203212
}
@@ -354,7 +363,12 @@ func runServices(ctx context.Context, dockerCli command.Cli, opts configOptions)
354363
return nil
355364
}
356365

357-
project, err := opts.ToProject(ctx, dockerCli, nil, cli.WithoutEnvironmentResolution)
366+
backend, err := compose.NewComposeService(dockerCli)
367+
if err != nil {
368+
return err
369+
}
370+
371+
project, _, err := opts.ProjectOptions.ToProject(ctx, dockerCli, backend, nil, cli.WithoutEnvironmentResolution)
358372
if err != nil {
359373
return err
360374
}
@@ -367,7 +381,12 @@ func runServices(ctx context.Context, dockerCli command.Cli, opts configOptions)
367381
}
368382

369383
func runVolumes(ctx context.Context, dockerCli command.Cli, opts configOptions) error {
370-
project, err := opts.ToProject(ctx, dockerCli, nil, cli.WithoutEnvironmentResolution)
384+
backend, err := compose.NewComposeService(dockerCli)
385+
if err != nil {
386+
return err
387+
}
388+
389+
project, _, err := opts.ProjectOptions.ToProject(ctx, dockerCli, backend, nil, cli.WithoutEnvironmentResolution)
371390
if err != nil {
372391
return err
373392
}
@@ -378,7 +397,12 @@ func runVolumes(ctx context.Context, dockerCli command.Cli, opts configOptions)
378397
}
379398

380399
func runNetworks(ctx context.Context, dockerCli command.Cli, opts configOptions) error {
381-
project, err := opts.ToProject(ctx, dockerCli, nil, cli.WithoutEnvironmentResolution)
400+
backend, err := compose.NewComposeService(dockerCli)
401+
if err != nil {
402+
return err
403+
}
404+
405+
project, _, err := opts.ProjectOptions.ToProject(ctx, dockerCli, backend, nil, cli.WithoutEnvironmentResolution)
382406
if err != nil {
383407
return err
384408
}
@@ -389,7 +413,12 @@ func runNetworks(ctx context.Context, dockerCli command.Cli, opts configOptions)
389413
}
390414

391415
func runModels(ctx context.Context, dockerCli command.Cli, opts configOptions) error {
392-
project, err := opts.ToProject(ctx, dockerCli, nil, cli.WithoutEnvironmentResolution)
416+
backend, err := compose.NewComposeService(dockerCli)
417+
if err != nil {
418+
return err
419+
}
420+
421+
project, _, err := opts.ProjectOptions.ToProject(ctx, dockerCli, backend, nil, cli.WithoutEnvironmentResolution)
393422
if err != nil {
394423
return err
395424
}
@@ -406,7 +435,13 @@ func runHash(ctx context.Context, dockerCli command.Cli, opts configOptions) err
406435
if opts.hash != "*" {
407436
services = append(services, strings.Split(opts.hash, ",")...)
408437
}
409-
project, err := opts.ToProject(ctx, dockerCli, nil, cli.WithoutEnvironmentResolution)
438+
439+
backend, err := compose.NewComposeService(dockerCli)
440+
if err != nil {
441+
return err
442+
}
443+
444+
project, _, err := opts.ProjectOptions.ToProject(ctx, dockerCli, backend, nil, cli.WithoutEnvironmentResolution)
410445
if err != nil {
411446
return err
412447
}
@@ -441,7 +476,13 @@ func runHash(ctx context.Context, dockerCli command.Cli, opts configOptions) err
441476

442477
func runProfiles(ctx context.Context, dockerCli command.Cli, opts configOptions, services []string) error {
443478
set := map[string]struct{}{}
444-
project, err := opts.ToProject(ctx, dockerCli, services, cli.WithoutEnvironmentResolution)
479+
480+
backend, err := compose.NewComposeService(dockerCli)
481+
if err != nil {
482+
return err
483+
}
484+
485+
project, err := opts.ToProject(ctx, dockerCli, backend, services)
445486
if err != nil {
446487
return err
447488
}
@@ -462,7 +503,12 @@ func runProfiles(ctx context.Context, dockerCli command.Cli, opts configOptions,
462503
}
463504

464505
func runConfigImages(ctx context.Context, dockerCli command.Cli, opts configOptions, services []string) error {
465-
project, err := opts.ToProject(ctx, dockerCli, services, cli.WithoutEnvironmentResolution)
506+
backend, err := compose.NewComposeService(dockerCli)
507+
if err != nil {
508+
return err
509+
}
510+
511+
project, err := opts.ToProject(ctx, dockerCli, backend, services)
466512
if err != nil {
467513
return err
468514
}
@@ -499,7 +545,12 @@ func runVariables(ctx context.Context, dockerCli command.Cli, opts configOptions
499545
}
500546

501547
func runEnvironment(ctx context.Context, dockerCli command.Cli, opts configOptions, services []string) error {
502-
project, err := opts.ToProject(ctx, dockerCli, services)
548+
backend, err := compose.NewComposeService(dockerCli)
549+
if err != nil {
550+
return err
551+
}
552+
553+
project, err := opts.ToProject(ctx, dockerCli, backend, services)
503554
if err != nil {
504555
return err
505556
}

cmd/compose/publish.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,12 @@ func publishCommand(p *ProjectOptions, dockerCli command.Cli, backendOptions *Ba
6969
}
7070

7171
func runPublish(ctx context.Context, dockerCli command.Cli, backendOptions *BackendOptions, opts publishOptions, repository string) error {
72-
project, metrics, err := opts.ToProject(ctx, dockerCli, nil)
72+
backend, err := compose.NewComposeService(dockerCli, backendOptions.Options...)
73+
if err != nil {
74+
return err
75+
}
76+
77+
project, metrics, err := opts.ToProject(ctx, dockerCli, backend, nil)
7378
if err != nil {
7479
return err
7580
}
@@ -78,10 +83,6 @@ func runPublish(ctx context.Context, dockerCli command.Cli, backendOptions *Back
7883
return errors.New("cannot publish compose file with local includes")
7984
}
8085

81-
backend, err := compose.NewComposeService(dockerCli, backendOptions.Options...)
82-
if err != nil {
83-
return err
84-
}
8586
return backend.Publish(ctx, project, repository, api.PublishOptions{
8687
ResolveImageDigests: opts.resolveImageDigests || opts.app,
8788
Application: opts.app,

0 commit comments

Comments
 (0)