We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Docker repository from build args
**Is your feature request related to a problem? Please describe.**

As we build images for our internal registry, the last part of the image name is usually derived from the git branch. Currently there is no easy way to incorporate this value into the built image name, besides the version tag.

**Describe the solution you'd like**

By allowing to interpolate build args into the repository field value, in the same manner as for image tags, we solve this problem.

**Describe alternatives you've considered**

Discarded the idea to have more predefined values based on environmental facts from things like git etc, in favour of a general solution where you can provide the required information as environment variable values.



--- 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/docker/goals/package_image.py`
Content:
```
1 # Copyright 2021 Pants project contributors (see CONTRIBUTORS.md).
2 # Licensed under the Apache License, Version 2.0 (see LICENSE).
3 from __future__ import annotations
4 
5 import logging
6 from dataclasses import dataclass
7 from os import path
8 
9 from pants.backend.docker.registries import DockerRegistries
10 from pants.backend.docker.subsystems.docker_options import DockerOptions
11 from pants.backend.docker.target_types import (
12     DockerImageSourceField,
13     DockerImageTagsField,
14     DockerRegistriesField,
15     DockerRepositoryField,
16 )
17 from pants.backend.docker.util_rules.docker_binary import DockerBinary
18 from pants.backend.docker.util_rules.docker_build_context import (
19     DockerBuildContext,
20     DockerBuildContextRequest,
21     DockerVersionContext,
22 )
23 from pants.core.goals.package import BuiltPackage, BuiltPackageArtifact, PackageFieldSet
24 from pants.core.goals.run import RunFieldSet
25 from pants.engine.process import Process, ProcessResult
26 from pants.engine.rules import Get, collect_rules, rule
27 from pants.engine.unions import UnionRule
28 from pants.util.strutil import bullet_list, pluralize
29 
30 logger = logging.getLogger(__name__)
31 
32 
33 class DockerImageTagValueError(ValueError):
34     pass
35 
36 
37 class DockerRepositoryNameError(ValueError):
38     pass
39 
40 
41 @dataclass(frozen=True)
42 class BuiltDockerImage(BuiltPackageArtifact):
43     tags: tuple[str, ...] = ()
44 
45     @classmethod
46     def create(cls, tags: tuple[str, ...]) -> BuiltDockerImage:
47         tags_string = tags[0] if len(tags) == 1 else (f"\n{bullet_list(tags)}")
48         return cls(
49             tags=tags,
50             relpath=None,
51             extra_log_lines=(
52                 f"Built docker {pluralize(len(tags), 'image', False)}: {tags_string}",
53             ),
54         )
55 
56 
57 @dataclass(frozen=True)
58 class DockerFieldSet(PackageFieldSet, RunFieldSet):
59     required_fields = (DockerImageSourceField,)
60 
61     registries: DockerRegistriesField
62     repository: DockerRepositoryField
63     tags: DockerImageTagsField
64 
65     def format_tag(self, tag: str, version_context: DockerVersionContext) -> str:
66         try:
67             return tag.format(**version_context)
68         except (KeyError, ValueError) as e:
69             msg = (
70                 "Invalid tag value for the `image_tags` field of the `docker_image` target at "
71                 f"{self.address}: {tag!r}.\n\n"
72             )
73             if isinstance(e, KeyError):
74                 msg += f"The placeholder {e} is unknown."
75                 if version_context:
76                     msg += f' Try with one of: {", ".join(version_context.keys())}.'
77                 else:
78                     msg += (
79                         " There are currently no known placeholders to use. These placeholders "
80                         "can come from `[docker].build_args` or parsed FROM instructions of "
81                         "your `Dockerfile`."
82                     )
83             else:
84                 msg += str(e)
85             raise DockerImageTagValueError(msg) from e
86 
87     def format_repository(self, default_repository: str) -> str:
88         directory = path.basename(self.address.spec_path)
89         parent_directory = path.basename(path.dirname(self.address.spec_path))
90         repository_fmt = self.repository.value or default_repository
91         try:
92             return repository_fmt.format(
93                 name=self.address.target_name,
94                 directory=directory,
95                 parent_directory=parent_directory,
96             )
97         except KeyError as e:
98             if self.repository.value:
99                 source = "`repository` field of the `docker_image` target " f"at {self.address}"
100             else:
101                 source = "`[docker].default_repository` configuration option"
102 
103             raise DockerRepositoryNameError(
104                 f"Invalid value for the {source}: {repository_fmt!r}. Unknown placeholder: {e}.\n\n"
105                 f"You may only reference any of `name`, `directory` or `parent_directory`."
106             ) from e
107 
108     def image_refs(
109         self,
110         default_repository: str,
111         registries: DockerRegistries,
112         version_context: DockerVersionContext,
113     ) -> tuple[str, ...]:
114         """The image refs are the full image name, including any registry and version tag.
115 
116         In the Docker world, the term `tag` is used both for what we here prefer to call the image
117         `ref`, as well as for the image version, or tag, that is at the end of the image name
118         separated with a colon. By introducing the image `ref` we can retain the use of `tag` for
119         the version part of the image name.
120 
121         Returns all image refs to apply to the Docker image, on the form:
122 
123             [<registry>/]<repository-name>[:<tag>]
124 
125         Where the `<repository-name>` may have contain any number of separating slashes `/`,
126         depending on the `default_repository` from configuration or the `repository` field
127         on the target `docker_image`.
128 
129         This method will always return a non-empty tuple.
130         """
131         repository = self.format_repository(default_repository)
132         image_names = tuple(
133             ":".join(s for s in [repository, self.format_tag(tag, version_context)] if s)
134             for tag in self.tags.value or ()
135         )
136 
137         registries_options = tuple(registries.get(*(self.registries.value or [])))
138         if not registries_options:
139             # The image name is also valid as image ref without registry.
140             return image_names
141 
142         return tuple(
143             "/".join([registry.address, image_name])
144             for image_name in image_names
145             for registry in registries_options
146         )
147 
148 
149 @rule
150 async def build_docker_image(
151     field_set: DockerFieldSet,
152     options: DockerOptions,
153     docker: DockerBinary,
154 ) -> BuiltPackage:
155     context = await Get(
156         DockerBuildContext,
157         DockerBuildContextRequest(
158             address=field_set.address,
159             build_upstream_images=True,
160         ),
161     )
162 
163     tags = field_set.image_refs(
164         default_repository=options.default_repository,
165         registries=options.registries(),
166         version_context=context.version_context,
167     )
168 
169     result = await Get(
170         ProcessResult,
171         Process,
172         docker.build_image(
173             build_args=context.build_args,
174             digest=context.digest,
175             dockerfile=context.dockerfile,
176             env=context.env,
177             tags=tags,
178         ),
179     )
180 
181     logger.debug(
182         f"Docker build output for {tags[0]}:\n"
183         f"{result.stdout.decode()}\n"
184         f"{result.stderr.decode()}"
185     )
186 
187     return BuiltPackage(
188         result.output_digest,
189         (BuiltDockerImage.create(tags),),
190     )
191 
192 
193 def rules():
194     return [
195         *collect_rules(),
196         UnionRule(PackageFieldSet, DockerFieldSet),
197         UnionRule(RunFieldSet, DockerFieldSet),
198     ]
199
```
--- 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/docker/goals/package_image.py b/src/python/pants/backend/docker/goals/package_image.py
--- a/src/python/pants/backend/docker/goals/package_image.py
+++ b/src/python/pants/backend/docker/goals/package_image.py
@@ -5,6 +5,7 @@
 import logging
 from dataclasses import dataclass
 from os import path
+from typing import Any, Mapping
 
 from pants.backend.docker.registries import DockerRegistries
 from pants.backend.docker.subsystems.docker_options import DockerOptions
@@ -73,7 +74,7 @@
             if isinstance(e, KeyError):
                 msg += f"The placeholder {e} is unknown."
                 if version_context:
-                    msg += f' Try with one of: {", ".join(version_context.keys())}.'
+                    msg += f' Try with one of: {", ".join(sorted(version_context.keys()))}.'
                 else:
                     msg += (
                         " There are currently no known placeholders to use. These placeholders "
@@ -84,26 +85,35 @@
                 msg += str(e)
             raise DockerImageTagValueError(msg) from e
 
-    def format_repository(self, default_repository: str) -> str:
-        directory = path.basename(self.address.spec_path)
-        parent_directory = path.basename(path.dirname(self.address.spec_path))
+    def format_repository(
+        self, default_repository: str, repository_context: Mapping[str, Any]
+    ) -> str:
+        fmt_context = dict(
+            directory=path.basename(self.address.spec_path),
+            name=self.address.target_name,
+            parent_directory=path.basename(path.dirname(self.address.spec_path)),
+            **repository_context,
+        )
         repository_fmt = self.repository.value or default_repository
+
         try:
-            return repository_fmt.format(
-                name=self.address.target_name,
-                directory=directory,
-                parent_directory=parent_directory,
-            )
-        except KeyError as e:
+            return repository_fmt.format(**fmt_context)
+        except (KeyError, ValueError) as e:
             if self.repository.value:
-                source = "`repository` field of the `docker_image` target " f"at {self.address}"
+                source = f"`repository` field of the `docker_image` target at {self.address}"
             else:
                 source = "`[docker].default_repository` configuration option"
 
-            raise DockerRepositoryNameError(
-                f"Invalid value for the {source}: {repository_fmt!r}. Unknown placeholder: {e}.\n\n"
-                f"You may only reference any of `name`, `directory` or `parent_directory`."
-            ) from e
+            msg = f"Invalid value for the {source}: {repository_fmt!r}.\n\n"
+
+            if isinstance(e, KeyError):
+                msg += (
+                    f"The placeholder {e} is unknown. "
+                    f'Try with one of: {", ".join(sorted(fmt_context.keys()))}.'
+                )
+            else:
+                msg += str(e)
+            raise DockerRepositoryNameError(msg) from e
 
     def image_refs(
         self,
@@ -122,13 +132,17 @@
 
             [<registry>/]<repository-name>[:<tag>]
 
-        Where the `<repository-name>` may have contain any number of separating slashes `/`,
-        depending on the `default_repository` from configuration or the `repository` field
-        on the target `docker_image`.
+        Where the `<repository-name>` may contain any number of separating slashes `/`, depending on
+        the `default_repository` from configuration or the `repository` field on the target
+        `docker_image`.
 
         This method will always return a non-empty tuple.
         """
-        repository = self.format_repository(default_repository)
+        repository_context = {}
+        if "build_args" in version_context:
+            repository_context["build_args"] = version_context["build_args"]
+
+        repository = self.format_repository(default_repository, repository_context)
         image_names = tuple(
             ":".join(s for s in [repository, self.format_tag(tag, version_context)] if s)
             for tag in self.tags.value or ()

{"golden_diff": "diff --git a/src/python/pants/backend/docker/goals/package_image.py b/src/python/pants/backend/docker/goals/package_image.py\n--- a/src/python/pants/backend/docker/goals/package_image.py\n+++ b/src/python/pants/backend/docker/goals/package_image.py\n@@ -5,6 +5,7 @@\n import logging\n from dataclasses import dataclass\n from os import path\n+from typing import Any, Mapping\n \n from pants.backend.docker.registries import DockerRegistries\n from pants.backend.docker.subsystems.docker_options import DockerOptions\n@@ -73,7 +74,7 @@\n             if isinstance(e, KeyError):\n                 msg += f\"The placeholder {e} is unknown.\"\n                 if version_context:\n-                    msg += f' Try with one of: {\", \".join(version_context.keys())}.'\n+                    msg += f' Try with one of: {\", \".join(sorted(version_context.keys()))}.'\n                 else:\n                     msg += (\n                         \" There are currently no known placeholders to use. These placeholders \"\n@@ -84,26 +85,35 @@\n                 msg += str(e)\n             raise DockerImageTagValueError(msg) from e\n \n-    def format_repository(self, default_repository: str) -> str:\n-        directory = path.basename(self.address.spec_path)\n-        parent_directory = path.basename(path.dirname(self.address.spec_path))\n+    def format_repository(\n+        self, default_repository: str, repository_context: Mapping[str, Any]\n+    ) -> str:\n+        fmt_context = dict(\n+            directory=path.basename(self.address.spec_path),\n+            name=self.address.target_name,\n+            parent_directory=path.basename(path.dirname(self.address.spec_path)),\n+            **repository_context,\n+        )\n         repository_fmt = self.repository.value or default_repository\n+\n         try:\n-            return repository_fmt.format(\n-                name=self.address.target_name,\n-                directory=directory,\n-                parent_directory=parent_directory,\n-            )\n-        except KeyError as e:\n+            return repository_fmt.format(**fmt_context)\n+        except (KeyError, ValueError) as e:\n             if self.repository.value:\n-                source = \"`repository` field of the `docker_image` target \" f\"at {self.address}\"\n+                source = f\"`repository` field of the `docker_image` target at {self.address}\"\n             else:\n                 source = \"`[docker].default_repository` configuration option\"\n \n-            raise DockerRepositoryNameError(\n-                f\"Invalid value for the {source}: {repository_fmt!r}. Unknown placeholder: {e}.\\n\\n\"\n-                f\"You may only reference any of `name`, `directory` or `parent_directory`.\"\n-            ) from e\n+            msg = f\"Invalid value for the {source}: {repository_fmt!r}.\\n\\n\"\n+\n+            if isinstance(e, KeyError):\n+                msg += (\n+                    f\"The placeholder {e} is unknown. \"\n+                    f'Try with one of: {\", \".join(sorted(fmt_context.keys()))}.'\n+                )\n+            else:\n+                msg += str(e)\n+            raise DockerRepositoryNameError(msg) from e\n \n     def image_refs(\n         self,\n@@ -122,13 +132,17 @@\n \n             [<registry>/]<repository-name>[:<tag>]\n \n-        Where the `<repository-name>` may have contain any number of separating slashes `/`,\n-        depending on the `default_repository` from configuration or the `repository` field\n-        on the target `docker_image`.\n+        Where the `<repository-name>` may contain any number of separating slashes `/`, depending on\n+        the `default_repository` from configuration or the `repository` field on the target\n+        `docker_image`.\n \n         This method will always return a non-empty tuple.\n         \"\"\"\n-        repository = self.format_repository(default_repository)\n+        repository_context = {}\n+        if \"build_args\" in version_context:\n+            repository_context[\"build_args\"] = version_context[\"build_args\"]\n+\n+        repository = self.format_repository(default_repository, repository_context)\n         image_names = tuple(\n             \":\".join(s for s in [repository, self.format_tag(tag, version_context)] if s)\n             for tag in self.tags.value or ()\n", "issue": "Docker repository from build args\n**Is your feature request related to a problem? Please describe.**\r\n\r\nAs we build images for our internal registry, the last part of the image name is usually derived from the git branch. Currently there is no easy way to incorporate this value into the built image name, besides the version tag.\r\n\r\n**Describe the solution you'd like**\r\n\r\nBy allowing to interpolate build args into the repository field value, in the same manner as for image tags, we solve this problem.\r\n\r\n**Describe alternatives you've considered**\r\n\r\nDiscarded the idea to have more predefined values based on environmental facts from things like git etc, in favour of a general solution where you can provide the required information as environment variable values.\r\n\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).\nfrom __future__ import annotations\n\nimport logging\nfrom dataclasses import dataclass\nfrom os import path\n\nfrom pants.backend.docker.registries import DockerRegistries\nfrom pants.backend.docker.subsystems.docker_options import DockerOptions\nfrom pants.backend.docker.target_types import (\n    DockerImageSourceField,\n    DockerImageTagsField,\n    DockerRegistriesField,\n    DockerRepositoryField,\n)\nfrom pants.backend.docker.util_rules.docker_binary import DockerBinary\nfrom pants.backend.docker.util_rules.docker_build_context import (\n    DockerBuildContext,\n    DockerBuildContextRequest,\n    DockerVersionContext,\n)\nfrom pants.core.goals.package import BuiltPackage, BuiltPackageArtifact, PackageFieldSet\nfrom pants.core.goals.run import RunFieldSet\nfrom pants.engine.process import Process, ProcessResult\nfrom pants.engine.rules import Get, collect_rules, rule\nfrom pants.engine.unions import UnionRule\nfrom pants.util.strutil import bullet_list, pluralize\n\nlogger = logging.getLogger(__name__)\n\n\nclass DockerImageTagValueError(ValueError):\n    pass\n\n\nclass DockerRepositoryNameError(ValueError):\n    pass\n\n\n@dataclass(frozen=True)\nclass BuiltDockerImage(BuiltPackageArtifact):\n    tags: tuple[str, ...] = ()\n\n    @classmethod\n    def create(cls, tags: tuple[str, ...]) -> BuiltDockerImage:\n        tags_string = tags[0] if len(tags) == 1 else (f\"\\n{bullet_list(tags)}\")\n        return cls(\n            tags=tags,\n            relpath=None,\n            extra_log_lines=(\n                f\"Built docker {pluralize(len(tags), 'image', False)}: {tags_string}\",\n            ),\n        )\n\n\n@dataclass(frozen=True)\nclass DockerFieldSet(PackageFieldSet, RunFieldSet):\n    required_fields = (DockerImageSourceField,)\n\n    registries: DockerRegistriesField\n    repository: DockerRepositoryField\n    tags: DockerImageTagsField\n\n    def format_tag(self, tag: str, version_context: DockerVersionContext) -> str:\n        try:\n            return tag.format(**version_context)\n        except (KeyError, ValueError) as e:\n            msg = (\n                \"Invalid tag value for the `image_tags` field of the `docker_image` target at \"\n                f\"{self.address}: {tag!r}.\\n\\n\"\n            )\n            if isinstance(e, KeyError):\n                msg += f\"The placeholder {e} is unknown.\"\n                if version_context:\n                    msg += f' Try with one of: {\", \".join(version_context.keys())}.'\n                else:\n                    msg += (\n                        \" There are currently no known placeholders to use. These placeholders \"\n                        \"can come from `[docker].build_args` or parsed FROM instructions of \"\n                        \"your `Dockerfile`.\"\n                    )\n            else:\n                msg += str(e)\n            raise DockerImageTagValueError(msg) from e\n\n    def format_repository(self, default_repository: str) -> str:\n        directory = path.basename(self.address.spec_path)\n        parent_directory = path.basename(path.dirname(self.address.spec_path))\n        repository_fmt = self.repository.value or default_repository\n        try:\n            return repository_fmt.format(\n                name=self.address.target_name,\n                directory=directory,\n                parent_directory=parent_directory,\n            )\n        except KeyError as e:\n            if self.repository.value:\n                source = \"`repository` field of the `docker_image` target \" f\"at {self.address}\"\n            else:\n                source = \"`[docker].default_repository` configuration option\"\n\n            raise DockerRepositoryNameError(\n                f\"Invalid value for the {source}: {repository_fmt!r}. Unknown placeholder: {e}.\\n\\n\"\n                f\"You may only reference any of `name`, `directory` or `parent_directory`.\"\n            ) from e\n\n    def image_refs(\n        self,\n        default_repository: str,\n        registries: DockerRegistries,\n        version_context: DockerVersionContext,\n    ) -> tuple[str, ...]:\n        \"\"\"The image refs are the full image name, including any registry and version tag.\n\n        In the Docker world, the term `tag` is used both for what we here prefer to call the image\n        `ref`, as well as for the image version, or tag, that is at the end of the image name\n        separated with a colon. By introducing the image `ref` we can retain the use of `tag` for\n        the version part of the image name.\n\n        Returns all image refs to apply to the Docker image, on the form:\n\n            [<registry>/]<repository-name>[:<tag>]\n\n        Where the `<repository-name>` may have contain any number of separating slashes `/`,\n        depending on the `default_repository` from configuration or the `repository` field\n        on the target `docker_image`.\n\n        This method will always return a non-empty tuple.\n        \"\"\"\n        repository = self.format_repository(default_repository)\n        image_names = tuple(\n            \":\".join(s for s in [repository, self.format_tag(tag, version_context)] if s)\n            for tag in self.tags.value or ()\n        )\n\n        registries_options = tuple(registries.get(*(self.registries.value or [])))\n        if not registries_options:\n            # The image name is also valid as image ref without registry.\n            return image_names\n\n        return tuple(\n            \"/\".join([registry.address, image_name])\n            for image_name in image_names\n            for registry in registries_options\n        )\n\n\n@rule\nasync def build_docker_image(\n    field_set: DockerFieldSet,\n    options: DockerOptions,\n    docker: DockerBinary,\n) -> BuiltPackage:\n    context = await Get(\n        DockerBuildContext,\n        DockerBuildContextRequest(\n            address=field_set.address,\n            build_upstream_images=True,\n        ),\n    )\n\n    tags = field_set.image_refs(\n        default_repository=options.default_repository,\n        registries=options.registries(),\n        version_context=context.version_context,\n    )\n\n    result = await Get(\n        ProcessResult,\n        Process,\n        docker.build_image(\n            build_args=context.build_args,\n            digest=context.digest,\n            dockerfile=context.dockerfile,\n            env=context.env,\n            tags=tags,\n        ),\n    )\n\n    logger.debug(\n        f\"Docker build output for {tags[0]}:\\n\"\n        f\"{result.stdout.decode()}\\n\"\n        f\"{result.stderr.decode()}\"\n    )\n\n    return BuiltPackage(\n        result.output_digest,\n        (BuiltDockerImage.create(tags),),\n    )\n\n\ndef rules():\n    return [\n        *collect_rules(),\n        UnionRule(PackageFieldSet, DockerFieldSet),\n        UnionRule(RunFieldSet, DockerFieldSet),\n    ]\n", "path": "src/python/pants/backend/docker/goals/package_image.py"}], "after_files": [{"content": "# Copyright 2021 Pants project contributors (see CONTRIBUTORS.md).\n# Licensed under the Apache License, Version 2.0 (see LICENSE).\nfrom __future__ import annotations\n\nimport logging\nfrom dataclasses import dataclass\nfrom os import path\nfrom typing import Any, Mapping\n\nfrom pants.backend.docker.registries import DockerRegistries\nfrom pants.backend.docker.subsystems.docker_options import DockerOptions\nfrom pants.backend.docker.target_types import (\n    DockerImageSourceField,\n    DockerImageTagsField,\n    DockerRegistriesField,\n    DockerRepositoryField,\n)\nfrom pants.backend.docker.util_rules.docker_binary import DockerBinary\nfrom pants.backend.docker.util_rules.docker_build_context import (\n    DockerBuildContext,\n    DockerBuildContextRequest,\n    DockerVersionContext,\n)\nfrom pants.core.goals.package import BuiltPackage, BuiltPackageArtifact, PackageFieldSet\nfrom pants.core.goals.run import RunFieldSet\nfrom pants.engine.process import Process, ProcessResult\nfrom pants.engine.rules import Get, collect_rules, rule\nfrom pants.engine.unions import UnionRule\nfrom pants.util.strutil import bullet_list, pluralize\n\nlogger = logging.getLogger(__name__)\n\n\nclass DockerImageTagValueError(ValueError):\n    pass\n\n\nclass DockerRepositoryNameError(ValueError):\n    pass\n\n\n@dataclass(frozen=True)\nclass BuiltDockerImage(BuiltPackageArtifact):\n    tags: tuple[str, ...] = ()\n\n    @classmethod\n    def create(cls, tags: tuple[str, ...]) -> BuiltDockerImage:\n        tags_string = tags[0] if len(tags) == 1 else (f\"\\n{bullet_list(tags)}\")\n        return cls(\n            tags=tags,\n            relpath=None,\n            extra_log_lines=(\n                f\"Built docker {pluralize(len(tags), 'image', False)}: {tags_string}\",\n            ),\n        )\n\n\n@dataclass(frozen=True)\nclass DockerFieldSet(PackageFieldSet, RunFieldSet):\n    required_fields = (DockerImageSourceField,)\n\n    registries: DockerRegistriesField\n    repository: DockerRepositoryField\n    tags: DockerImageTagsField\n\n    def format_tag(self, tag: str, version_context: DockerVersionContext) -> str:\n        try:\n            return tag.format(**version_context)\n        except (KeyError, ValueError) as e:\n            msg = (\n                \"Invalid tag value for the `image_tags` field of the `docker_image` target at \"\n                f\"{self.address}: {tag!r}.\\n\\n\"\n            )\n            if isinstance(e, KeyError):\n                msg += f\"The placeholder {e} is unknown.\"\n                if version_context:\n                    msg += f' Try with one of: {\", \".join(sorted(version_context.keys()))}.'\n                else:\n                    msg += (\n                        \" There are currently no known placeholders to use. These placeholders \"\n                        \"can come from `[docker].build_args` or parsed FROM instructions of \"\n                        \"your `Dockerfile`.\"\n                    )\n            else:\n                msg += str(e)\n            raise DockerImageTagValueError(msg) from e\n\n    def format_repository(\n        self, default_repository: str, repository_context: Mapping[str, Any]\n    ) -> str:\n        fmt_context = dict(\n            directory=path.basename(self.address.spec_path),\n            name=self.address.target_name,\n            parent_directory=path.basename(path.dirname(self.address.spec_path)),\n            **repository_context,\n        )\n        repository_fmt = self.repository.value or default_repository\n\n        try:\n            return repository_fmt.format(**fmt_context)\n        except (KeyError, ValueError) as e:\n            if self.repository.value:\n                source = f\"`repository` field of the `docker_image` target at {self.address}\"\n            else:\n                source = \"`[docker].default_repository` configuration option\"\n\n            msg = f\"Invalid value for the {source}: {repository_fmt!r}.\\n\\n\"\n\n            if isinstance(e, KeyError):\n                msg += (\n                    f\"The placeholder {e} is unknown. \"\n                    f'Try with one of: {\", \".join(sorted(fmt_context.keys()))}.'\n                )\n            else:\n                msg += str(e)\n            raise DockerRepositoryNameError(msg) from e\n\n    def image_refs(\n        self,\n        default_repository: str,\n        registries: DockerRegistries,\n        version_context: DockerVersionContext,\n    ) -> tuple[str, ...]:\n        \"\"\"The image refs are the full image name, including any registry and version tag.\n\n        In the Docker world, the term `tag` is used both for what we here prefer to call the image\n        `ref`, as well as for the image version, or tag, that is at the end of the image name\n        separated with a colon. By introducing the image `ref` we can retain the use of `tag` for\n        the version part of the image name.\n\n        Returns all image refs to apply to the Docker image, on the form:\n\n            [<registry>/]<repository-name>[:<tag>]\n\n        Where the `<repository-name>` may contain any number of separating slashes `/`, depending on\n        the `default_repository` from configuration or the `repository` field on the target\n        `docker_image`.\n\n        This method will always return a non-empty tuple.\n        \"\"\"\n        repository_context = {}\n        if \"build_args\" in version_context:\n            repository_context[\"build_args\"] = version_context[\"build_args\"]\n\n        repository = self.format_repository(default_repository, repository_context)\n        image_names = tuple(\n            \":\".join(s for s in [repository, self.format_tag(tag, version_context)] if s)\n            for tag in self.tags.value or ()\n        )\n\n        registries_options = tuple(registries.get(*(self.registries.value or [])))\n        if not registries_options:\n            # The image name is also valid as image ref without registry.\n            return image_names\n\n        return tuple(\n            \"/\".join([registry.address, image_name])\n            for image_name in image_names\n            for registry in registries_options\n        )\n\n\n@rule\nasync def build_docker_image(\n    field_set: DockerFieldSet,\n    options: DockerOptions,\n    docker: DockerBinary,\n) -> BuiltPackage:\n    context = await Get(\n        DockerBuildContext,\n        DockerBuildContextRequest(\n            address=field_set.address,\n            build_upstream_images=True,\n        ),\n    )\n\n    tags = field_set.image_refs(\n        default_repository=options.default_repository,\n        registries=options.registries(),\n        version_context=context.version_context,\n    )\n\n    result = await Get(\n        ProcessResult,\n        Process,\n        docker.build_image(\n            build_args=context.build_args,\n            digest=context.digest,\n            dockerfile=context.dockerfile,\n            env=context.env,\n            tags=tags,\n        ),\n    )\n\n    logger.debug(\n        f\"Docker build output for {tags[0]}:\\n\"\n        f\"{result.stdout.decode()}\\n\"\n        f\"{result.stderr.decode()}\"\n    )\n\n    return BuiltPackage(\n        result.output_digest,\n        (BuiltDockerImage.create(tags),),\n    )\n\n\ndef rules():\n    return [\n        *collect_rules(),\n        UnionRule(PackageFieldSet, DockerFieldSet),\n        UnionRule(RunFieldSet, DockerFieldSet),\n    ]\n", "path": "src/python/pants/backend/docker/goals/package_image.py"}]}