Repository: docker/compose

Issue:
[BUG] `Include.env_file` content can't be used for secrets from environment variable
### Description

Setup: A parent compose project includes a child compose compose project which has `secrets` from `environment` defined.

When including an env file in the `include` directive, the content is not considered for the secret.
The error is then `environment variable "MY_SECRET" required by secret "parent_my-secret" is not set`

It however works if the env file is included directly via the `--env-file` flag.

I wonder if this is intentional behavior, at least I was surprised by this. 
And there is no restriction regarding secrets mentioned in the `include` directive docs: https://docs.docker.com/reference/compose-file/include/#env_file

### Steps To Reproduce

Create a file `child/compose.yaml` with content (service prints env var and secret, which will be both defined in env-vars)
```yaml
secrets:
  my-secret:
   environment: 'MY_SECRET'

services:
  my-service:
    image: alpine
    secrets:
      - my-secret
    environment:
      - MY_ENV
    command: "/bin/sh -c 'echo env-var: $MY_ENV && cat /run/secrets/my-secret'"
```
Then create the parent compose file at `compose.yaml` with content
```yaml
name: parent

include:
  - path: child/compose.yaml
    env_file:
      - secret.env
```
And the env file `secret.env` with content
```yaml
MY_SECRET='super-secret'
MY_ENV='foo'
```

So the resulting file tree is
```
├── child
│   └── compose.yaml
├── compose.yaml
└── secret.env
```

Then execute in the root folder of this tree `docker compose up`, it will output

```sh
[+] Running 1/2
 ✔ Network parent_default         Created                                                                                                                                                                                                             
 ⠋ Container parent-my-service-1  Creating                                                                                                                                                                                                             
environment variable "MY_SECRET" required by secret "parent_my-secret" is not set
```
Repeating the command `docker compose up` will start successfully (seems like the secret env var is only checked on container creation, which is a bit confusing):
```sh
[+] Running 1/1
 ✔ Container d9402f3d9545_parent-my-service-1  Created                                                                                                                                                                                                0.0s 
Attaching to d9402f3d9545_parent-my-service-1
d9402f3d9545_parent-my-service-1  | env-var: foo
d9402f3d9545_parent-my-service-1  | cat: can't open '/run/secrets/my-secret': No such file or directory
d9402f3d9545_parent-my-service-1 exited with code 1
```

Clean up with `docker compose down -v` and execute `docker compose --env-file secret.env up` will give the desired result:
```sh
[+] Running 2/2
 ✔ Network parent_default         Created                                                                                                                                                                                                             
 ✔ Container parent-my-service-1  Created                                                                                                                                                                                                             
Attaching to my-service-1
my-service-1  | env-var: foo
my-service-1  | super-secret
my-service-1 exited with code 0
```

### Compose Version

```Text
Docker Compose version v2.33.1
```

### Docker Environment

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

Server:
 Containers: 23
  Running: 11
  Paused: 0
  Stopped: 12
 Images: 41
 Server Version: 28.0.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: io.containerd.runc.v2 runc
 Default Runtime: runc
 Init Binary: docker-init
 containerd version: bcc810d6b9066471b0b6fa75f557a15a1cbf31bb
 runc version: v1.2.4-0-g6c52b3f
 init version: de40ad0
 Security Options:
  apparmor
  seccomp
   Profile: builtin
  cgroupns
 Kernel Version: 6.8.0-52-generic
 Operating System: Ubuntu 22.04.5 LTS
 OSType: linux
 Architecture: x86_64
 CPUs: 12
 Total Memory: 30.15GiB
 Name: GB-HO-50
 ID: b7121279-c046-406f-85a0-dade9f473530
 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?

_No response_

Discussion:
@ndeloof: Thanks for reporting this issue with a reproduction example, which I used as a test case on my PR 😇 
see https://github.com/docker/compose/pull/12635

Solution patch:
diff --git a/cmd/cmdtrace/cmd_span.go b/cmd/cmdtrace/cmd_span.go
index 1ad435b2..9226fa13 100644
--- a/cmd/cmdtrace/cmd_span.go
+++ b/cmd/cmdtrace/cmd_span.go
@@ -115,13 +115,14 @@ func wrapRunE(c *cobra.Command, cmdSpan trace.Span, tracingShutdown tracing.Shut
 	}
 }
 
-// commandName returns the path components for a given command.
+// commandName returns the path components for a given command,
+// in reverse alphabetical order for consistent usage metrics.
 //
 // The root Compose command and anything before (i.e. "docker")
 // are not included.
 //
 // For example:
-//   - docker compose alpha watch -> [alpha, watch]
+//   - docker compose alpha watch -> [watch, alpha]
 //   - docker-compose up -> [up]
 func commandName(cmd *cobra.Command) []string {
 	var name []string
diff --git a/pkg/compose/secrets.go b/pkg/compose/secrets.go
index 4a38141a..2ebaa0e6 100644
--- a/pkg/compose/secrets.go
+++ b/pkg/compose/secrets.go
@@ -45,11 +45,15 @@ func (s *composeService) injectSecrets(ctx context.Context, project *types.Proje
 			config.Target = "/run/secrets/" + config.Target
 		}
 
-		env, ok := project.Environment[file.Environment]
-		if !ok {
-			return fmt.Errorf("environment variable %q required by secret %q is not set", file.Environment, file.Name)
+		content := file.Content
+		if content == "" {
+			env, ok := project.Environment[file.Environment]
+			if !ok {
+				return fmt.Errorf("environment variable %q required by secret %q is not set", file.Environment, file.Name)
+			}
+			content = env
 		}
-		b, err := createTar(env, types.FileReferenceConfig(config))
+		b, err := createTar(content, types.FileReferenceConfig(config))
 		if err != nil {
 			return err
 		}
diff --git a/pkg/e2e/fixtures/env-secret/child/compose.yaml b/pkg/e2e/fixtures/env-secret/child/compose.yaml
new file mode 100644
index 00000000..6e4ab821
--- /dev/null
+++ b/pkg/e2e/fixtures/env-secret/child/compose.yaml
@@ -0,0 +1,10 @@
+services:
+  included:
+    image: alpine
+    secrets:
+      - my-secret
+    command: cat /run/secrets/my-secret
+
+secrets:
+  my-secret:
+    environment: 'MY_SECRET'
diff --git a/pkg/e2e/fixtures/env-secret/compose.yaml b/pkg/e2e/fixtures/env-secret/compose.yaml
index be801888..51052d36 100644
--- a/pkg/e2e/fixtures/env-secret/compose.yaml
+++ b/pkg/e2e/fixtures/env-secret/compose.yaml
@@ -1,3 +1,8 @@
+include:
+  - path: child/compose.yaml
+    env_file:
+      - secret.env
+
 services:
   foo:
     image: alpine
diff --git a/pkg/e2e/fixtures/env-secret/secret.env b/pkg/e2e/fixtures/env-secret/secret.env
new file mode 100644
index 00000000..a195fd53
--- /dev/null
+++ b/pkg/e2e/fixtures/env-secret/secret.env
@@ -0,0 +1 @@
+MY_SECRET='this-is-secret'



Test patch:
diff --git a/cmd/cmdtrace/cmd_span_test.go b/cmd/cmdtrace/cmd_span_test.go
index 3d661eda..75073307 100644
--- a/cmd/cmdtrace/cmd_span_test.go
+++ b/cmd/cmdtrace/cmd_span_test.go
@@ -20,6 +20,8 @@ import (
 	"reflect"
 	"testing"
 
+	commands "github.com/docker/compose/v2/cmd/compose"
+	"github.com/spf13/cobra"
 	flag "github.com/spf13/pflag"
 )
 
@@ -61,3 +63,50 @@ func TestGetFlags(t *testing.T) {
 		})
 	}
 }
+
+func TestCommandName(t *testing.T) {
+	tests := []struct {
+		name     string
+		setupCmd func() *cobra.Command
+		want     []string
+	}{
+		{
+			name: "docker compose alpha watch -> [watch, alpha]",
+			setupCmd: func() *cobra.Command {
+				dockerCmd := &cobra.Command{Use: "docker"}
+				composeCmd := &cobra.Command{Use: commands.PluginName}
+				alphaCmd := &cobra.Command{Use: "alpha"}
+				watchCmd := &cobra.Command{Use: "watch"}
+
+				dockerCmd.AddCommand(composeCmd)
+				composeCmd.AddCommand(alphaCmd)
+				alphaCmd.AddCommand(watchCmd)
+
+				return watchCmd
+			},
+			want: []string{"watch", "alpha"},
+		},
+		{
+			name: "docker-compose up -> [up]",
+			setupCmd: func() *cobra.Command {
+				dockerComposeCmd := &cobra.Command{Use: commands.PluginName}
+				upCmd := &cobra.Command{Use: "up"}
+
+				dockerComposeCmd.AddCommand(upCmd)
+
+				return upCmd
+			},
+			want: []string{"up"},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			cmd := tt.setupCmd()
+			got := commandName(cmd)
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("commandName() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
diff --git a/pkg/e2e/secrets_test.go b/pkg/e2e/secrets_test.go
index 3b9daf10..3e389511 100644
--- a/pkg/e2e/secrets_test.go
+++ b/pkg/e2e/secrets_test.go
@@ -41,3 +41,13 @@ func TestSecretFromEnv(t *testing.T) {
 		res.Assert(t, icmd.Expected{Out: "-r--r-----    1 1005     1005"})
 	})
 }
+
+func TestSecretFromInclude(t *testing.T) {
+	c := NewParallelCLI(t)
+	defer c.cleanupWithDown(t, "env-secret-include")
+
+	t.Run("compose run", func(t *testing.T) {
+		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/env-secret/compose.yaml", "run", "included")
+		res.Assert(t, icmd.Expected{Out: "this-is-secret"})
+	})
+}