Repository: pantsbuild/pants

Issue:
`adhoc_tool` ignores the `extra_env_vars` field
The code just isn't there 🤷 

Affects 2.16.


Discussion:

Resolving patch:
diff --git a/src/python/pants/backend/adhoc/adhoc_tool.py b/src/python/pants/backend/adhoc/adhoc_tool.py
--- a/src/python/pants/backend/adhoc/adhoc_tool.py
+++ b/src/python/pants/backend/adhoc/adhoc_tool.py
@@ -7,6 +7,7 @@
 from pants.backend.adhoc.target_types import (
     AdhocToolArgumentsField,
     AdhocToolExecutionDependenciesField,
+    AdhocToolExtraEnvVarsField,
     AdhocToolLogOutputField,
     AdhocToolOutputDependenciesField,
     AdhocToolOutputDirectoriesField,
@@ -162,7 +163,7 @@ async def run_in_sandbox_request(
         append_only_caches=FrozenDict.frozen(merged_extras.append_only_caches),
         output_files=output_files,
         output_directories=output_directories,
-        fetch_env_vars=(),
+        fetch_env_vars=target.get(AdhocToolExtraEnvVarsField).value or (),
         supplied_env_var_values=FrozenDict(extra_env),
         log_on_process_errors=None,
         log_output=target[AdhocToolLogOutputField].value,


Test patch:
diff --git a/src/python/pants/backend/adhoc/adhoc_tool_test.py b/src/python/pants/backend/adhoc/adhoc_tool_test.py
--- a/src/python/pants/backend/adhoc/adhoc_tool_test.py
+++ b/src/python/pants/backend/adhoc/adhoc_tool_test.py
@@ -10,7 +10,8 @@
 
 from pants.backend.adhoc.adhoc_tool import GenerateFilesFromAdhocToolRequest
 from pants.backend.adhoc.adhoc_tool import rules as adhoc_tool_rules
-from pants.backend.adhoc.target_types import AdhocToolTarget
+from pants.backend.adhoc.run_system_binary import rules as run_system_binary_rules
+from pants.backend.adhoc.target_types import AdhocToolTarget, SystemBinaryTarget
 from pants.backend.python.goals.run_python_source import rules as run_python_source_rules
 from pants.backend.python.target_types import PythonSourceTarget
 from pants.core.target_types import ArchiveTarget, FilesGeneratorTarget
@@ -39,12 +40,14 @@ def rule_runner() -> RuleRunner:
             *source_files.rules(),
             *core_target_type_rules(),
             *run_python_source_rules(),
+            *run_system_binary_rules(),
             QueryRule(GeneratedSources, [GenerateFilesFromAdhocToolRequest]),
             QueryRule(Process, [AdhocProcessRequest]),
             QueryRule(SourceFiles, [SourceFilesRequest]),
             QueryRule(TransitiveTargets, [TransitiveTargetsRequest]),
         ],
         target_types=[
+            SystemBinaryTarget,
             AdhocToolTarget,
             ArchiveTarget,
             FilesGeneratorTarget,
@@ -224,3 +227,34 @@ def test_working_directory_special_values(
         Address("src", target_name="run_fruitcake"),
         expected_contents={os.path.join(file_location, "fruitcake.txt"): "fruitcake\n"},
     )
+
+
+def test_env_vars(rule_runner: RuleRunner) -> None:
+    envvar_value = "clang"
+    rule_runner.write_files(
+        {
+            "src/BUILD": dedent(
+                f"""\
+                system_binary(
+                    name="bash",
+                    binary_name="bash",
+                )
+
+                adhoc_tool(
+                  name="envvars",
+                  runnable=":bash",
+                  args=['-c', 'echo $ENVVAR > out.log'],
+                  output_files=["out.log"],
+                  extra_env_vars=["ENVVAR={envvar_value}"],
+                  root_output_directory=".",
+                )
+                """
+            ),
+        }
+    )
+
+    assert_adhoc_tool_result(
+        rule_runner,
+        Address("src", target_name="envvars"),
+        expected_contents={"out.log": f"{envvar_value}\n"},
+    )
diff --git a/src/python/pants/backend/adhoc/run_system_binary_integration_test.py b/src/python/pants/backend/adhoc/run_system_binary_integration_test.py
--- a/src/python/pants/backend/adhoc/run_system_binary_integration_test.py
+++ b/src/python/pants/backend/adhoc/run_system_binary_integration_test.py
@@ -130,3 +130,37 @@ def test_runnable_dependencies() -> None:
         ]
         result = run_pants(args)
         assert "[INFO] I am a duck." in result.stderr.strip()
+
+
+def test_external_env_vars() -> None:
+    sources = {
+        "src/BUILD": dedent(
+            """\
+
+            system_binary(
+                name="bash",
+                binary_name="bash",
+            )
+
+            adhoc_tool(
+                name="adhoc",
+                runnable=":bash",
+                args=["-c", "echo $ENVVAR"],
+                log_output=True,
+                stdout="stdout",
+                extra_env_vars=["ENVVAR"],
+            )
+            """
+        ),
+    }
+
+    with setup_tmpdir(sources) as tmpdir:
+        args = [
+            "--backend-packages=['pants.backend.experimental.adhoc',]",
+            f"--source-root-patterns=['{tmpdir}/src']",
+            "export-codegen",
+            f"{tmpdir}/src:adhoc",
+        ]
+        extra_env = {"ENVVAR": "clang"}
+        result = run_pants(args, extra_env=extra_env)
+        assert "[INFO] clang" in result.stderr.strip()
diff --git a/src/python/pants/backend/shell/util_rules/shell_command_integration_test.py b/src/python/pants/backend/shell/util_rules/shell_command_integration_test.py
--- a/src/python/pants/backend/shell/util_rules/shell_command_integration_test.py
+++ b/src/python/pants/backend/shell/util_rules/shell_command_integration_test.py
@@ -27,3 +27,28 @@ def test_passthrough_args() -> None:
         ] + [f"arg{i}" for i in range(1, 4)]
         result = run_pants(args)
         assert "arg1 arg2 arg3" in result.stdout.strip()
+
+
+def test_external_env_vars() -> None:
+    sources = {
+        "src/BUILD": dedent(
+            """\
+            shell_command(
+                name="env-vars",
+                command='echo $ENVVAR',
+                extra_env_vars=["ENVVAR"],
+                log_output=True,
+            )
+            """
+        ),
+    }
+    with setup_tmpdir(sources) as tmpdir:
+        args = [
+            "--backend-packages=pants.backend.shell",
+            f"--source-root-patterns=['{tmpdir}/src']",
+            "export-codegen",
+            f"{tmpdir}/src:env-vars",
+        ]
+        extra_env = {"ENVVAR": "clang"}
+        result = run_pants(args, extra_env=extra_env)
+        assert extra_env["ENVVAR"] in result.stderr
diff --git a/src/python/pants/backend/shell/util_rules/shell_command_test.py b/src/python/pants/backend/shell/util_rules/shell_command_test.py
--- a/src/python/pants/backend/shell/util_rules/shell_command_test.py
+++ b/src/python/pants/backend/shell/util_rules/shell_command_test.py
@@ -803,3 +803,29 @@ def test_missing_tool_called(
         )
 
     assert "requires the names of any external commands" in caplog.text
+
+
+def test_env_vars(rule_runner: RuleRunner) -> None:
+    envvar_value = "clang"
+    rule_runner.write_files(
+        {
+            "src/BUILD": dedent(
+                f"""\
+                shell_command(
+                  name="envvars",
+                  tools=[],
+                  command='echo $ENVVAR > out.log',
+                  output_files=["out.log"],
+                  extra_env_vars=["ENVVAR={envvar_value}"],
+                  root_output_directory=".",
+                )
+                """
+            ),
+        }
+    )
+
+    assert_shell_command_result(
+        rule_runner,
+        Address("src", target_name="envvars"),
+        expected_contents={"out.log": f"{envvar_value}\n"},
+    )