Repository: docker/compose
File: cmd/compose/generate.go (Go)

Code before:
func generateCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
	opts := reverseOptions{
		ProjectOptions: p,
	}

	cmd := &cobra.Command{
		Use:   "generate [OPTIONS] [CONTAINERS...]",
		Short: "EXPERIMENTAL - Generate a Compose file from existing containers",
		PreRunE: Adapt(func(ctx context.Context, args []string) error {
			return nil
		}),
		RunE: Adapt(func(ctx context.Context, args []string) error {
			return runGenerate(ctx, backend, opts, args)
		}),
	}

	cmd.Flags().StringVar(&opts.ProjectName, "name", "", "Project name to set in the Compose file")
	cmd.Flags().StringVar(&opts.ProjectDir, "project-dir", "", "Directory to use for the project")
	cmd.Flags().StringVar(&opts.Format, "format", "yaml", "Format the output. Values: [yaml | json]")
	return cmd
}

func runGenerate(ctx context.Context, backend api.Service, opts reverseOptions, containers []string) error {
	_, _ = fmt.Fprintln(os.Stderr, "reverse command is EXPERIMENTAL")
	if len(containers) == 0 {
		return fmt.Errorf("at least one service must be specified")
	}
	project, err := backend.Generate(ctx, api.GenerateOptions{
		Containers: containers,
	})
	if err != nil {
		return err
	}
	var content []byte
	switch opts.Format {
	case "json":
		content, err = project.MarshalJSON()
	case "yaml":
		content, err = project.MarshalYAML()
	default:
		return fmt.Errorf("unsupported format %q", opts.Format)
	}
	if err != nil {
		return err
	}
	fmt.Println(string(content))

	return nil
}


Review comment (suggestion):
nit: maybe `container` instead of `service`? just thinking about how users will be thinking of their input as containers and not yet in the compose world of services

Code after:
func generateCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
	opts := generateOptions{
		ProjectOptions: p,
	}

	cmd := &cobra.Command{
		Use:   "generate [OPTIONS] [CONTAINERS...]",
		Short: "EXPERIMENTAL - Generate a Compose file from existing containers",
		PreRunE: Adapt(func(ctx context.Context, args []string) error {
			return nil
		}),
		RunE: Adapt(func(ctx context.Context, args []string) error {
			return runGenerate(ctx, backend, opts, args)
		}),
	}

	cmd.Flags().StringVar(&opts.ProjectName, "name", "", "Project name to set in the Compose file")
	cmd.Flags().StringVar(&opts.ProjectDir, "project-dir", "", "Directory to use for the project")
	cmd.Flags().StringVar(&opts.Format, "format", "yaml", "Format the output. Values: [yaml | json]")
	return cmd
}

func runGenerate(ctx context.Context, backend api.Service, opts generateOptions, containers []string) error {
	_, _ = fmt.Fprintln(os.Stderr, "generate command is EXPERIMENTAL")
	if len(containers) == 0 {
		return fmt.Errorf("at least one container must be specified")
	}
	project, err := backend.Generate(ctx, api.GenerateOptions{
		Containers:  containers,
		ProjectName: opts.ProjectName,
	})
	if err != nil {
		return err
	}
	var content []byte
	switch opts.Format {
	case "json":
		content, err = project.MarshalJSON()
	case "yaml":
		content, err = project.MarshalYAML()
	default:
		return fmt.Errorf("unsupported format %q", opts.Format)
	}
	if err != nil {
		return err
	}
	fmt.Println(string(content))

	return nil
}