We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
`shfmt` assumes downloaded executable will be named `shfmt_{version}_{platform}`, and breaks if it isn't
**Describe the bug**

To reduce network transfer & flakiness during CI, we've pre-cached all the "external" tools used by Pants in our executor container. As part of this I've overridden the `url_template` for each tool to use a `file://` URL. The URL-paths I ended up using in the image were "simplified" from the defaults - for example, I have:
```toml
[shfmt]
url_template = "file:///opt/pants-tools/shfmt/{version}/shfmt"
```

When CI runs with this config, it fails with:
```
Error launching process: Os { code: 2, kind: NotFound, message: "No such file or directory" }
```

I `ssh`'d into one of the executors that hit this failure, and looked inside the failing sandbox. There I saw:
1. The `shfmt` binary _was_ in the sandbox, and runnable
2. According to `__run.sh`, Pants was trying to invoke `./shfmt_v3.2.4_linux_amd64` instead of plain `./shfmt`

I believe this is happening because the `shfmt` subsystem defines `generate_exe` to hard-code the same naming pattern as is used in the default `url_pattern`: https://github.com/pantsbuild/pants/blob/ac9e27b142b14f079089522c1175a9e380291100/src/python/pants/backend/shell/lint/shfmt/subsystem.py#L56-L58

I think things would operate as expected if we deleted that `generate_exe` override, since the `shfmt` download is the executable itself.

**Pants version**

2.15.0rc4

**OS**

Observed on Linux

**Additional info**

https://app.toolchain.com/organizations/color/repos/color/builds/pants_run_2023_02_15_12_48_26_897_660d20c55cc041fbb63374c79a4402b0/


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/python/pants/backend/shell/lint/shfmt/subsystem.py`
Content:
```
1 # Copyright 2021 Pants project contributors (see CONTRIBUTORS.md).
2 # Licensed under the Apache License, Version 2.0 (see LICENSE).
3 
4 from __future__ import annotations
5 
6 import os.path
7 from typing import Iterable
8 
9 from pants.core.util_rules.config_files import ConfigFilesRequest
10 from pants.core.util_rules.external_tool import TemplatedExternalTool
11 from pants.engine.platform import Platform
12 from pants.option.option_types import ArgsListOption, BoolOption, SkipOption
13 from pants.util.strutil import softwrap
14 
15 
16 class Shfmt(TemplatedExternalTool):
17     options_scope = "shfmt"
18     name = "shfmt"
19     help = "An autoformatter for shell scripts (https://github.com/mvdan/sh)."
20 
21     default_version = "v3.6.0"
22     default_known_versions = [
23         "v3.2.4|macos_arm64 |e70fc42e69debe3e400347d4f918630cdf4bf2537277d672bbc43490387508ec|2998546",
24         "v3.2.4|macos_x86_64|43a0461a1b54070ddc04fbbf1b78f7861ee39a65a61f5466d15a39c4aba4f917|2980208",
25         "v3.2.4|linux_arm64 |6474d9cc08a1c9fe2ef4be7a004951998e3067d46cf55a011ddd5ff7bfab3de6|2752512",
26         "v3.2.4|linux_x86_64|3f5a47f8fec27fae3e06d611559a2063f5d27e4b9501171dde9959b8c60a3538|2797568",
27         "v3.6.0|macos_arm64 |633f242246ee0a866c5f5df25cbf61b6af0d5e143555aca32950059cf13d91e0|3065202",
28         "v3.6.0|macos_x86_64|b8c9c025b498e2816b62f0b717f6032e9ab49e725a45b8205f52f66318f17185|3047552",
29         "v3.6.0|linux_arm64 |fb1cf0af3dbe9aac7d98e38e3c7426765208ecfe23cb2da51037bb234776fd70|2818048",
30         "v3.6.0|linux_x86_64|5741a02a641de7e56b8da170e71a97e58050d66a3cf485fb268d6a5a8bb74afb|2850816",
31     ]
32 
33     default_url_template = (
34         "https://github.com/mvdan/sh/releases/download/{version}/shfmt_{version}_{platform}"
35     )
36     default_url_platform_mapping = {
37         "macos_arm64": "darwin_arm64",
38         "macos_x86_64": "darwin_amd64",
39         "linux_arm64": "linux_arm64",
40         "linux_x86_64": "linux_amd64",
41     }
42 
43     skip = SkipOption("fmt", "lint")
44     args = ArgsListOption(example="-i 2")
45     config_discovery = BoolOption(
46         default=True,
47         advanced=True,
48         help=softwrap(
49             """
50             If true, Pants will include all relevant `.editorconfig` files during runs.
51             See https://editorconfig.org.
52             """
53         ),
54     )
55 
56     def generate_exe(self, plat: Platform) -> str:
57         plat_str = self.default_url_platform_mapping[plat.value]
58         return f"./shfmt_{self.version}_{plat_str}"
59 
60     def config_request(self, dirs: Iterable[str]) -> ConfigFilesRequest:
61         # Refer to https://editorconfig.org/#file-location for how config files are discovered.
62         candidates = (os.path.join(d, ".editorconfig") for d in ("", *dirs))
63         return ConfigFilesRequest(
64             discovery=self.config_discovery,
65             check_content={fp: b"[*.sh]" for fp in candidates},
66         )
67
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/python/pants/backend/shell/lint/shfmt/subsystem.py b/src/python/pants/backend/shell/lint/shfmt/subsystem.py
--- a/src/python/pants/backend/shell/lint/shfmt/subsystem.py
+++ b/src/python/pants/backend/shell/lint/shfmt/subsystem.py
@@ -8,7 +8,6 @@
 
 from pants.core.util_rules.config_files import ConfigFilesRequest
 from pants.core.util_rules.external_tool import TemplatedExternalTool
-from pants.engine.platform import Platform
 from pants.option.option_types import ArgsListOption, BoolOption, SkipOption
 from pants.util.strutil import softwrap
 
@@ -53,10 +52,6 @@
         ),
     )
 
-    def generate_exe(self, plat: Platform) -> str:
-        plat_str = self.default_url_platform_mapping[plat.value]
-        return f"./shfmt_{self.version}_{plat_str}"
-
     def config_request(self, dirs: Iterable[str]) -> ConfigFilesRequest:
         # Refer to https://editorconfig.org/#file-location for how config files are discovered.
         candidates = (os.path.join(d, ".editorconfig") for d in ("", *dirs))

{"golden_diff": "diff --git a/src/python/pants/backend/shell/lint/shfmt/subsystem.py b/src/python/pants/backend/shell/lint/shfmt/subsystem.py\n--- a/src/python/pants/backend/shell/lint/shfmt/subsystem.py\n+++ b/src/python/pants/backend/shell/lint/shfmt/subsystem.py\n@@ -8,7 +8,6 @@\n \n from pants.core.util_rules.config_files import ConfigFilesRequest\n from pants.core.util_rules.external_tool import TemplatedExternalTool\n-from pants.engine.platform import Platform\n from pants.option.option_types import ArgsListOption, BoolOption, SkipOption\n from pants.util.strutil import softwrap\n \n@@ -53,10 +52,6 @@\n         ),\n     )\n \n-    def generate_exe(self, plat: Platform) -> str:\n-        plat_str = self.default_url_platform_mapping[plat.value]\n-        return f\"./shfmt_{self.version}_{plat_str}\"\n-\n     def config_request(self, dirs: Iterable[str]) -> ConfigFilesRequest:\n         # Refer to https://editorconfig.org/#file-location for how config files are discovered.\n         candidates = (os.path.join(d, \".editorconfig\") for d in (\"\", *dirs))\n", "issue": "`shfmt` assumes downloaded executable will be named `shfmt_{version}_{platform}`, and breaks if it isn't\n**Describe the bug**\r\n\r\nTo reduce network transfer & flakiness during CI, we've pre-cached all the \"external\" tools used by Pants in our executor container. As part of this I've overridden the `url_template` for each tool to use a `file://` URL. The URL-paths I ended up using in the image were \"simplified\" from the defaults - for example, I have:\r\n```toml\r\n[shfmt]\r\nurl_template = \"file:///opt/pants-tools/shfmt/{version}/shfmt\"\r\n```\r\n\r\nWhen CI runs with this config, it fails with:\r\n```\r\nError launching process: Os { code: 2, kind: NotFound, message: \"No such file or directory\" }\r\n```\r\n\r\nI `ssh`'d into one of the executors that hit this failure, and looked inside the failing sandbox. There I saw:\r\n1. The `shfmt` binary _was_ in the sandbox, and runnable\r\n2. According to `__run.sh`, Pants was trying to invoke `./shfmt_v3.2.4_linux_amd64` instead of plain `./shfmt`\r\n\r\nI believe this is happening because the `shfmt` subsystem defines `generate_exe` to hard-code the same naming pattern as is used in the default `url_pattern`: https://github.com/pantsbuild/pants/blob/ac9e27b142b14f079089522c1175a9e380291100/src/python/pants/backend/shell/lint/shfmt/subsystem.py#L56-L58\r\n\r\nI think things would operate as expected if we deleted that `generate_exe` override, since the `shfmt` download is the executable itself.\r\n\r\n**Pants version**\r\n\r\n2.15.0rc4\r\n\r\n**OS**\r\n\r\nObserved on Linux\r\n\r\n**Additional info**\r\n\r\nhttps://app.toolchain.com/organizations/color/repos/color/builds/pants_run_2023_02_15_12_48_26_897_660d20c55cc041fbb63374c79a4402b0/\r\n\n", "before_files": [{"content": "# Copyright 2021 Pants project contributors (see CONTRIBUTORS.md).\n# Licensed under the Apache License, Version 2.0 (see LICENSE).\n\nfrom __future__ import annotations\n\nimport os.path\nfrom typing import Iterable\n\nfrom pants.core.util_rules.config_files import ConfigFilesRequest\nfrom pants.core.util_rules.external_tool import TemplatedExternalTool\nfrom pants.engine.platform import Platform\nfrom pants.option.option_types import ArgsListOption, BoolOption, SkipOption\nfrom pants.util.strutil import softwrap\n\n\nclass Shfmt(TemplatedExternalTool):\n    options_scope = \"shfmt\"\n    name = \"shfmt\"\n    help = \"An autoformatter for shell scripts (https://github.com/mvdan/sh).\"\n\n    default_version = \"v3.6.0\"\n    default_known_versions = [\n        \"v3.2.4|macos_arm64 |e70fc42e69debe3e400347d4f918630cdf4bf2537277d672bbc43490387508ec|2998546\",\n        \"v3.2.4|macos_x86_64|43a0461a1b54070ddc04fbbf1b78f7861ee39a65a61f5466d15a39c4aba4f917|2980208\",\n        \"v3.2.4|linux_arm64 |6474d9cc08a1c9fe2ef4be7a004951998e3067d46cf55a011ddd5ff7bfab3de6|2752512\",\n        \"v3.2.4|linux_x86_64|3f5a47f8fec27fae3e06d611559a2063f5d27e4b9501171dde9959b8c60a3538|2797568\",\n        \"v3.6.0|macos_arm64 |633f242246ee0a866c5f5df25cbf61b6af0d5e143555aca32950059cf13d91e0|3065202\",\n        \"v3.6.0|macos_x86_64|b8c9c025b498e2816b62f0b717f6032e9ab49e725a45b8205f52f66318f17185|3047552\",\n        \"v3.6.0|linux_arm64 |fb1cf0af3dbe9aac7d98e38e3c7426765208ecfe23cb2da51037bb234776fd70|2818048\",\n        \"v3.6.0|linux_x86_64|5741a02a641de7e56b8da170e71a97e58050d66a3cf485fb268d6a5a8bb74afb|2850816\",\n    ]\n\n    default_url_template = (\n        \"https://github.com/mvdan/sh/releases/download/{version}/shfmt_{version}_{platform}\"\n    )\n    default_url_platform_mapping = {\n        \"macos_arm64\": \"darwin_arm64\",\n        \"macos_x86_64\": \"darwin_amd64\",\n        \"linux_arm64\": \"linux_arm64\",\n        \"linux_x86_64\": \"linux_amd64\",\n    }\n\n    skip = SkipOption(\"fmt\", \"lint\")\n    args = ArgsListOption(example=\"-i 2\")\n    config_discovery = BoolOption(\n        default=True,\n        advanced=True,\n        help=softwrap(\n            \"\"\"\n            If true, Pants will include all relevant `.editorconfig` files during runs.\n            See https://editorconfig.org.\n            \"\"\"\n        ),\n    )\n\n    def generate_exe(self, plat: Platform) -> str:\n        plat_str = self.default_url_platform_mapping[plat.value]\n        return f\"./shfmt_{self.version}_{plat_str}\"\n\n    def config_request(self, dirs: Iterable[str]) -> ConfigFilesRequest:\n        # Refer to https://editorconfig.org/#file-location for how config files are discovered.\n        candidates = (os.path.join(d, \".editorconfig\") for d in (\"\", *dirs))\n        return ConfigFilesRequest(\n            discovery=self.config_discovery,\n            check_content={fp: b\"[*.sh]\" for fp in candidates},\n        )\n", "path": "src/python/pants/backend/shell/lint/shfmt/subsystem.py"}], "after_files": [{"content": "# Copyright 2021 Pants project contributors (see CONTRIBUTORS.md).\n# Licensed under the Apache License, Version 2.0 (see LICENSE).\n\nfrom __future__ import annotations\n\nimport os.path\nfrom typing import Iterable\n\nfrom pants.core.util_rules.config_files import ConfigFilesRequest\nfrom pants.core.util_rules.external_tool import TemplatedExternalTool\nfrom pants.option.option_types import ArgsListOption, BoolOption, SkipOption\nfrom pants.util.strutil import softwrap\n\n\nclass Shfmt(TemplatedExternalTool):\n    options_scope = \"shfmt\"\n    name = \"shfmt\"\n    help = \"An autoformatter for shell scripts (https://github.com/mvdan/sh).\"\n\n    default_version = \"v3.6.0\"\n    default_known_versions = [\n        \"v3.2.4|macos_arm64 |e70fc42e69debe3e400347d4f918630cdf4bf2537277d672bbc43490387508ec|2998546\",\n        \"v3.2.4|macos_x86_64|43a0461a1b54070ddc04fbbf1b78f7861ee39a65a61f5466d15a39c4aba4f917|2980208\",\n        \"v3.2.4|linux_arm64 |6474d9cc08a1c9fe2ef4be7a004951998e3067d46cf55a011ddd5ff7bfab3de6|2752512\",\n        \"v3.2.4|linux_x86_64|3f5a47f8fec27fae3e06d611559a2063f5d27e4b9501171dde9959b8c60a3538|2797568\",\n        \"v3.6.0|macos_arm64 |633f242246ee0a866c5f5df25cbf61b6af0d5e143555aca32950059cf13d91e0|3065202\",\n        \"v3.6.0|macos_x86_64|b8c9c025b498e2816b62f0b717f6032e9ab49e725a45b8205f52f66318f17185|3047552\",\n        \"v3.6.0|linux_arm64 |fb1cf0af3dbe9aac7d98e38e3c7426765208ecfe23cb2da51037bb234776fd70|2818048\",\n        \"v3.6.0|linux_x86_64|5741a02a641de7e56b8da170e71a97e58050d66a3cf485fb268d6a5a8bb74afb|2850816\",\n    ]\n\n    default_url_template = (\n        \"https://github.com/mvdan/sh/releases/download/{version}/shfmt_{version}_{platform}\"\n    )\n    default_url_platform_mapping = {\n        \"macos_arm64\": \"darwin_arm64\",\n        \"macos_x86_64\": \"darwin_amd64\",\n        \"linux_arm64\": \"linux_arm64\",\n        \"linux_x86_64\": \"linux_amd64\",\n    }\n\n    skip = SkipOption(\"fmt\", \"lint\")\n    args = ArgsListOption(example=\"-i 2\")\n    config_discovery = BoolOption(\n        default=True,\n        advanced=True,\n        help=softwrap(\n            \"\"\"\n            If true, Pants will include all relevant `.editorconfig` files during runs.\n            See https://editorconfig.org.\n            \"\"\"\n        ),\n    )\n\n    def config_request(self, dirs: Iterable[str]) -> ConfigFilesRequest:\n        # Refer to https://editorconfig.org/#file-location for how config files are discovered.\n        candidates = (os.path.join(d, \".editorconfig\") for d in (\"\", *dirs))\n        return ConfigFilesRequest(\n            discovery=self.config_discovery,\n            check_content={fp: b\"[*.sh]\" for fp in candidates},\n        )\n", "path": "src/python/pants/backend/shell/lint/shfmt/subsystem.py"}]}