Repository: docker/compose

Issue:
[BUG] config --variables doesn't honor --format flag
### Description

Providing the --variables and --format flag to the `config` command always results in printing in the human readable form.

A user would expect that when providing `--format json` the `config --variables` would produce a json output, but it currently doesn't 

### Steps To Reproduce


Using this oneliner:
```bash
printf '%s\n' \
"services:" \
"  test:" \
"    image: \${DOCKER_REGISTRY_BASE:-ubuntu/ubuntu:latest}" \
"    ports:" \
"      - \"\${BIND_ADDRESS:-127.0.0.1}:\${BIND_PORT:-8100}:8100\"" \
> /tmp/compose.yml &&
docker compose -f /tmp/compose.yml config --variables --format json
```

Output:
```
NAME                   REQUIRED            DEFAULT VALUE          ALTERNATE VALUE
DOCKER_REGISTRY_BASE   false               ubuntu/ubuntu:latest
BIND_PORT              false               8100
BIND_ADDRESS           false               127.0.0.1

```

Config example in case printf have some troubles working in your setup/terminal:
```yaml
services:
  test:
    image: ${DOCKER_REGISTRY_BASE:-ubuntu/ubuntu:latest}
    ports:
      - "${BIND_ADDRESS:-127.0.0.1}:${BIND_PORT:-8100}:8100"
```

### Compose Version

```Text
v2.35.1
```

### Docker Environment

```Text
Client: Docker Engine - Community
 Version:    28.1.1
 Context:    default
 Debug Mode: false
 Plugins:
  buildx: Docker Buildx (Docker Inc.)
    Version:  v0.23.0
    Path:     /usr/libexec/docker/cli-plugins/docker-buildx
  compose: Docker Compose (Docker Inc.)
    Version:  v2.35.1
    Path:     /usr/libexec/docker/cli-plugins/docker-compose

Server:
 Containers: 8
  Running: 5
  Paused: 0
  Stopped: 3
 Images: 40
 Server Version: 28.1.1
 Storage Driver: overlay2
  Backing Filesystem: extfs
  Supports d_type: true
  Using metacopy: false
  Native Overlay Diff: true
  userxattr: false
 Logging Driver: json-file
 Cgroup Driver: systemd
 Cgroup Version: 2
 Plugins:
  Volume: local
  Network: bridge host ipvlan macvlan null overlay
  Log: awslogs fluentd gcplogs gelf journald json-file local splunk syslog
 Swarm: inactive
 Runtimes: runc io.containerd.runc.v2
 Default Runtime: runc
 Init Binary: docker-init
 containerd version: 05044ec0a9a75232cad458027ca83437aae3f4da
 runc version: v1.2.5-0-g59923ef
 init version: de40ad0
 Security Options:
  apparmor
  seccomp
   Profile: builtin
  cgroupns
 Kernel Version: 6.11.0-24-generic
 Operating System: Ubuntu 24.04.2 LTS
 OSType: linux
 Architecture: x86_64
 CPUs: 20
 Total Memory: 31.02GiB
 Name: ale
 ID: c4b213ee-6b17-445a-9dc6-6566795b9c0a
 Docker Root Dir: /var/lib/docker
 Debug Mode: false
 Experimental: false
 Insecure Registries:
  ::1/128
  127.0.0.0/8
 Live Restore Enabled: false
```

### Anything else?

Expected output:
```json
{
    "BIND_ADDRESS": {
        "Name": "BIND_ADDRESS",
        "DefaultValue": "127.0.0.1",
        "PresenceValue": "",
        "Required": false
    },
    "BIND_PORT": {
        "Name": "BIND_PORT",
        "DefaultValue": "8100",
        "PresenceValue": "",
        "Required": false
    },
    "DOCKER_REGISTRY_BASE": {
        "Name": "DOCKER_REGISTRY_BASE",
        "DefaultValue": "ubuntu/ubuntu:latest",
        "PresenceValue": "",
        "Required": false
    }
}
```

Discussion:

Solution patch:
diff --git a/cmd/compose/config.go b/cmd/compose/config.go
index e3b85f43..a30c8fe7 100644
--- a/cmd/compose/config.go
+++ b/cmd/compose/config.go
@@ -124,12 +124,15 @@ func configCommand(p *ProjectOptions, dockerCli command.Cli) *cobra.Command {
 				return runEnvironment(ctx, dockerCli, opts, args)
 			}
 
+			if opts.Format == "" {
+				opts.Format = "yaml"
+			}
 			return runConfig(ctx, dockerCli, opts, args)
 		}),
 		ValidArgsFunction: completeServiceNames(dockerCli, p),
 	}
 	flags := cmd.Flags()
-	flags.StringVar(&opts.Format, "format", "yaml", "Format the output. Values: [yaml | json]")
+	flags.StringVar(&opts.Format, "format", "", "Format the output. Values: [yaml | json]")
 	flags.BoolVar(&opts.resolveImageDigests, "resolve-image-digests", false, "Pin image tags to digests")
 	flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Only validate the configuration, don't print anything")
 	flags.BoolVar(&opts.noInterpolate, "no-interpolate", false, "Don't interpolate environment variables")
@@ -408,7 +411,16 @@ func runVariables(ctx context.Context, dockerCli command.Cli, opts configOptions
 
 	variables := template.ExtractVariables(model, template.DefaultPattern)
 
-	return formatter.Print(variables, "", dockerCli.Out(), func(w io.Writer) {
+	if opts.Format == "yaml" {
+		result, err := yaml.Marshal(variables)
+		if err != nil {
+			return err
+		}
+		fmt.Print(string(result))
+		return nil
+	}
+
+	return formatter.Print(variables, opts.Format, dockerCli.Out(), func(w io.Writer) {
 		for name, variable := range variables {
 			_, _ = fmt.Fprintf(w, "%s\t%t\t%s\t%s\n", name, variable.Required, variable.DefaultValue, variable.PresenceValue)
 		}
diff --git a/docs/reference/compose_config.md b/docs/reference/compose_config.md
index 9e87efd2..78c1835a 100644
--- a/docs/reference/compose_config.md
+++ b/docs/reference/compose_config.md
@@ -15,7 +15,7 @@ the canonical format.
 |:--------------------------|:---------|:--------|:----------------------------------------------------------------------------|
 | `--dry-run`               | `bool`   |         | Execute command in dry run mode                                             |
 | `--environment`           | `bool`   |         | Print environment used for interpolation.                                   |
-| `--format`                | `string` | `yaml`  | Format the output. Values: [yaml \| json]                                   |
+| `--format`                | `string` |         | Format the output. Values: [yaml \| json]                                   |
 | `--hash`                  | `string` |         | Print the service config hash, one per line.                                |
 | `--images`                | `bool`   |         | Print the image names, one per line.                                        |
 | `--no-consistency`        | `bool`   |         | Don't check model consistency - warning: may produce invalid Compose output |
diff --git a/docs/reference/docker_compose_config.yaml b/docs/reference/docker_compose_config.yaml
index 15b1e7dc..7ec479b2 100644
--- a/docs/reference/docker_compose_config.yaml
+++ b/docs/reference/docker_compose_config.yaml
@@ -21,7 +21,6 @@ options:
       swarm: false
     - option: format
       value_type: string
-      default_value: yaml
       description: 'Format the output. Values: [yaml | json]'
       deprecated: false
       hidden: false



Test patch:
diff --git a/pkg/e2e/config_test.go b/pkg/e2e/config_test.go
index 6fef7bc0..15d3e3d9 100644
--- a/pkg/e2e/config_test.go
+++ b/pkg/e2e/config_test.go
@@ -46,4 +46,25 @@ func TestLocalComposeConfig(t *testing.T) {
 		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/config/compose.yaml", "--project-name", projectName, "config", "--no-interpolate")
 		res.Assert(t, icmd.Expected{Out: `- ${PORT:-8080}:80`})
 	})
+
+	t.Run("--variables --format json", func(t *testing.T) {
+		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/config/compose.yaml", "--project-name", projectName, "config", "--variables", "--format", "json")
+		res.Assert(t, icmd.Expected{Out: `{
+    "PORT": {
+        "Name": "PORT",
+        "DefaultValue": "8080",
+        "PresenceValue": "",
+        "Required": false
+    }
+}`})
+	})
+
+	t.Run("--variables --format yaml", func(t *testing.T) {
+		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/config/compose.yaml", "--project-name", projectName, "config", "--variables", "--format", "yaml")
+		res.Assert(t, icmd.Expected{Out: `PORT:
+    name: PORT
+    defaultvalue: "8080"
+    presencevalue: ""
+    required: false`})
+	})
 }