Skip to content

Commit 198b357

Browse files
gloursndeloof
authored andcommitted
Migrate CLI commands to use LoadProject API
Simplifying the codebase and eliminating duplicate backend creation. Signed-off-by: Guillaume Lours <[email protected]>
1 parent c53e5ee commit 198b357

File tree

12 files changed

+173
-117
lines changed

12 files changed

+173
-117
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
}
@@ -353,7 +362,12 @@ func runServices(ctx context.Context, dockerCli command.Cli, opts configOptions)
353362
return nil
354363
}
355364

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

368382
func runVolumes(ctx context.Context, dockerCli command.Cli, opts configOptions) error {
369-
project, err := opts.ToProject(ctx, dockerCli, nil, cli.WithoutEnvironmentResolution)
383+
backend, err := compose.NewComposeService(dockerCli)
384+
if err != nil {
385+
return err
386+
}
387+
388+
project, _, err := opts.ProjectOptions.ToProject(ctx, dockerCli, backend, nil, cli.WithoutEnvironmentResolution)
370389
if err != nil {
371390
return err
372391
}
@@ -377,7 +396,12 @@ func runVolumes(ctx context.Context, dockerCli command.Cli, opts configOptions)
377396
}
378397

379398
func runNetworks(ctx context.Context, dockerCli command.Cli, opts configOptions) error {
380-
project, err := opts.ToProject(ctx, dockerCli, nil, cli.WithoutEnvironmentResolution)
399+
backend, err := compose.NewComposeService(dockerCli)
400+
if err != nil {
401+
return err
402+
}
403+
404+
project, _, err := opts.ProjectOptions.ToProject(ctx, dockerCli, backend, nil, cli.WithoutEnvironmentResolution)
381405
if err != nil {
382406
return err
383407
}
@@ -388,7 +412,12 @@ func runNetworks(ctx context.Context, dockerCli command.Cli, opts configOptions)
388412
}
389413

390414
func runModels(ctx context.Context, dockerCli command.Cli, opts configOptions) error {
391-
project, err := opts.ToProject(ctx, dockerCli, nil, cli.WithoutEnvironmentResolution)
415+
backend, err := compose.NewComposeService(dockerCli)
416+
if err != nil {
417+
return err
418+
}
419+
420+
project, _, err := opts.ProjectOptions.ToProject(ctx, dockerCli, backend, nil, cli.WithoutEnvironmentResolution)
392421
if err != nil {
393422
return err
394423
}
@@ -405,7 +434,13 @@ func runHash(ctx context.Context, dockerCli command.Cli, opts configOptions) err
405434
if opts.hash != "*" {
406435
services = append(services, strings.Split(opts.hash, ",")...)
407436
}
408-
project, err := opts.ToProject(ctx, dockerCli, nil, cli.WithoutEnvironmentResolution)
437+
438+
backend, err := compose.NewComposeService(dockerCli)
439+
if err != nil {
440+
return err
441+
}
442+
443+
project, _, err := opts.ProjectOptions.ToProject(ctx, dockerCli, backend, nil, cli.WithoutEnvironmentResolution)
409444
if err != nil {
410445
return err
411446
}
@@ -440,7 +475,13 @@ func runHash(ctx context.Context, dockerCli command.Cli, opts configOptions) err
440475

441476
func runProfiles(ctx context.Context, dockerCli command.Cli, opts configOptions, services []string) error {
442477
set := map[string]struct{}{}
443-
project, err := opts.ToProject(ctx, dockerCli, services, cli.WithoutEnvironmentResolution)
478+
479+
backend, err := compose.NewComposeService(dockerCli)
480+
if err != nil {
481+
return err
482+
}
483+
484+
project, err := opts.ToProject(ctx, dockerCli, backend, services)
444485
if err != nil {
445486
return err
446487
}
@@ -461,7 +502,12 @@ func runProfiles(ctx context.Context, dockerCli command.Cli, opts configOptions,
461502
}
462503

463504
func runConfigImages(ctx context.Context, dockerCli command.Cli, opts configOptions, services []string) error {
464-
project, err := opts.ToProject(ctx, dockerCli, services, cli.WithoutEnvironmentResolution)
505+
backend, err := compose.NewComposeService(dockerCli)
506+
if err != nil {
507+
return err
508+
}
509+
510+
project, err := opts.ToProject(ctx, dockerCli, backend, services)
465511
if err != nil {
466512
return err
467513
}
@@ -498,7 +544,12 @@ func runVariables(ctx context.Context, dockerCli command.Cli, opts configOptions
498544
}
499545

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

0 commit comments

Comments
 (0)