Repository: pdm-project/pdm

Issue:
ENV is not inherited in nested composite scripts
- [x] I have searched the issue tracker and believe that this is not a duplicate.

Environment variables are inherited from composite scripts to regular commands, but not between nested composite scripts.

## Steps to reproduce

<!--Describe the minimized example of how to reproduce the bug-->

- Create three scripts:
  - `test` composite, which calls ...
  - `nested` composite, which calls ...
  - `foo` script
- Scripts `test` and `foo` define different values for `env.VAR`
  - optional: `nested` also defines a value for `env.VAR`
- Call the `test` script: `pdm run -v test`

```toml
[tool.pdm.scripts]
test.composite = ["nested"]
test.env.VAR = "test"
nested.composite = ["foo"]
# nested.env.VAR = "nested"
foo.cmd = "python -c \"print('${VAR}')\""
foo.env.VAR = "bar"
```

## Actual behavior

<!--A clear and concise description the result of the above steps-->

Running `test` when `env.VAR` is not overridden in `nested`:
```shell
Running <task test>: []
Running <task nested>: []
Running <task foo>: ['python', '-c', "print('${VAR}')"]
bar
```
In this case, `VAR` gets the original value defined in `foo`.

Running `test` after uncommenting `nested.env.VAR = "nested"`:
```shell
Running <task test>: []
Running <task nested>: []
Running <task foo>: ['python', '-c', "print('${VAR}')"]
nested
```
Now, we see the overridden value defined in `nested`.

## Expected behavior

<!--A clear and concise description of what you expected to happen.-->
In both cases, I would expect `VAR` to be `test`, as defined in the `test` script.

## Environment Information

```bash
PDM version:
  2.15.0
Python Interpreter:
  redacted/.venv/bin/python (3.12)
Project Root:
  redacted
Local Packages:
  
{
  "implementation_name": "cpython",
  "implementation_version": "3.12.0",
  "os_name": "posix",
  "platform_machine": "x86_64",
  "platform_release": "5.15.133.1-microsoft-standard-WSL2",
  "platform_system": "Linux",
  "platform_version": "#1 SMP Thu Oct 5 21:02:42 UTC 2023",
  "python_full_version": "3.12.0",
  "platform_python_implementation": "CPython",
  "python_version": "3.12",
  "sys_platform": "linux"
}
```

Discussion:

Patch:
diff --git a/news/2849.bugfix.md b/news/2849.bugfix.md
new file mode 100644
index 00000000..3bef6128
--- /dev/null
+++ b/news/2849.bugfix.md
@@ -0,0 +1,1 @@
+Fix env and other options being inherited in nested composite scripts.
\ No newline at end of file
diff --git a/src/pdm/cli/commands/run.py b/src/pdm/cli/commands/run.py
index 69f64f24..06d0891e 100644
--- a/src/pdm/cli/commands/run.py
+++ b/src/pdm/cli/commands/run.py
@@ -277,6 +277,8 @@ class TaskRunner:
             should_interpolate = should_interpolate or any(RE_PDM_PLACEHOLDER.search(script) for script in value)
             composite_code = 0
             keep_going = options.pop("keep_going", False) if options else False
+            if opts:
+                cast(dict, options).update(**exec_opts(options, opts))
             for script in value:
                 if should_interpolate:
                     script, _ = interpolate(script, args)


Test patch:
diff --git a/tests/cli/test_run.py b/tests/cli/test_run.py
index e97e5c0f..4b4e4af5 100644
--- a/tests/cli/test_run.py
+++ b/tests/cli/test_run.py
@@ -640,7 +640,15 @@ def test_composite_inherit_env(project, pdm, capfd, _echo):
             "cmd": "python echo.py Second VAR",
             "env": {"VAR": "42"},
         },
-        "test": {"composite": ["first", "second"], "env": {"VAR": "overriden"}},
+        "nested": {
+            "composite": ["third"],
+            "env": {"VAR": "42"},
+        },
+        "third": {
+            "cmd": "python echo.py Third VAR",
+            "env": {"VAR": "42"},
+        },
+        "test": {"composite": ["first", "second", "nested"], "env": {"VAR": "overriden"}},
     }
     project.pyproject.write()
     capfd.readouterr()
@@ -648,6 +656,7 @@ def test_composite_inherit_env(project, pdm, capfd, _echo):
     out, _ = capfd.readouterr()
     assert "First CALLED with VAR=overriden" in out
     assert "Second CALLED with VAR=overriden" in out
+    assert "Third CALLED with VAR=overriden" in out
 
 
 def test_composite_fail_on_first_missing_task(project, pdm, capfd, _echo):