Repository: pantsbuild/pants

Issue:
Make the search path for Docker configurable
Currently, the `docker` binary is searched for in a hard coded set of paths. If your binary is elsewhere, you're out of luck.

Introduce a new option to specify search path. Model after https://www.pantsbuild.org/docs/reference-shell-setup#section-executable-search-paths



Discussion:

Resolving patch:
diff --git a/src/python/pants/backend/docker/subsystems/docker_options.py b/src/python/pants/backend/docker/subsystems/docker_options.py
--- a/src/python/pants/backend/docker/subsystems/docker_options.py
+++ b/src/python/pants/backend/docker/subsystems/docker_options.py
@@ -3,15 +3,18 @@
 
 from __future__ import annotations
 
+import os
 import sys
 from textwrap import dedent
 from typing import cast
 
 from pants.backend.docker.registries import DockerRegistries
+from pants.engine.environment import Environment
 from pants.option.custom_types import shell_str
 from pants.option.errors import ParseError
 from pants.option.subsystem import Subsystem
 from pants.util.memo import memoized_method
+from pants.util.ordered_set import OrderedSet
 from pants.util.strutil import bullet_list
 
 doc_links = {
@@ -171,6 +174,19 @@ def register_options(cls, register):
             ),
         )
 
+        register(
+            "--executable-search-paths",
+            advanced=True,
+            type=list,
+            default=["<PATH>"],
+            metavar="<binary-paths>",
+            help=(
+                "The PATH value that will be used to find the Docker client and any tools required."
+                "\n\n"
+                'The special string `"<PATH>"` will expand to the contents of the PATH env var.'
+            ),
+        )
+
     @property
     def build_args(self) -> tuple[str, ...]:
         return tuple(sorted(set(self.options.build_args)))
@@ -198,3 +214,16 @@ def default_repository(self) -> str:
     @memoized_method
     def registries(self) -> DockerRegistries:
         return DockerRegistries.from_dict(self.options.registries)
+
+    @memoized_method
+    def executable_search_path(self, env: Environment) -> tuple[str, ...]:
+        def iter_path_entries():
+            for entry in self.options.executable_search_paths:
+                if entry == "<PATH>":
+                    path = env.get("PATH")
+                    if path:
+                        yield from path.split(os.pathsep)
+                else:
+                    yield entry
+
+        return tuple(OrderedSet(iter_path_entries()))
diff --git a/src/python/pants/backend/docker/util_rules/docker_binary.py b/src/python/pants/backend/docker/util_rules/docker_binary.py
--- a/src/python/pants/backend/docker/util_rules/docker_binary.py
+++ b/src/python/pants/backend/docker/util_rules/docker_binary.py
@@ -6,7 +6,9 @@
 from dataclasses import dataclass
 from typing import Mapping
 
+from pants.backend.docker.subsystems.docker_options import DockerOptions
 from pants.backend.docker.util_rules.docker_build_args import DockerBuildArgs
+from pants.engine.environment import Environment, EnvironmentRequest
 from pants.engine.fs import Digest
 from pants.engine.process import (
     BinaryNotFoundError,
@@ -97,10 +99,14 @@ class DockerBinaryRequest:
 
 
 @rule(desc="Finding the `docker` binary", level=LogLevel.DEBUG)
-async def find_docker(docker_request: DockerBinaryRequest) -> DockerBinary:
+async def find_docker(
+    docker_request: DockerBinaryRequest, docker_options: DockerOptions
+) -> DockerBinary:
+    env = await Get(Environment, EnvironmentRequest(["PATH"]))
+    search_path = docker_options.executable_search_path(env)
     request = BinaryPathRequest(
         binary_name="docker",
-        search_path=docker_request.search_path,
+        search_path=search_path or docker_request.search_path,
         test=BinaryPathTest(args=["-v"]),
     )
     paths = await Get(BinaryPaths, BinaryPathRequest, request)


Test patch: