We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
"export" goal docs have unclosed call out
**Describe the bug**

The warning callout about "exporting tools requires ..." seems to be unclosed:

- https://www.pantsbuild.org/2.18/reference/goals/export
- https://www.pantsbuild.org/2.19/reference/goals/export
- https://www.pantsbuild.org/2.20/reference/goals/export

![image](https://github.com/pantsbuild/pants/assets/1203825/5cfc04ee-27a7-49ba-a695-80d601aeace6)

**Pants version**
2.18 onwards

**OS**
macOS

**Additional info**
Introduced in #20604 

--- 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/core/goals/export.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 itertools
7 import os
8 from dataclasses import dataclass
9 from typing import Iterable, Mapping, Sequence, cast
10 
11 from pants.base.build_root import BuildRoot
12 from pants.core.goals.generate_lockfiles import (
13     GenerateToolLockfileSentinel,
14     KnownUserResolveNames,
15     KnownUserResolveNamesRequest,
16     UnrecognizedResolveNamesError,
17 )
18 from pants.core.util_rules.distdir import DistDir
19 from pants.core.util_rules.environments import _warn_on_non_local_environments
20 from pants.engine.collection import Collection
21 from pants.engine.console import Console
22 from pants.engine.env_vars import EnvironmentVars, EnvironmentVarsRequest
23 from pants.engine.environment import EnvironmentName
24 from pants.engine.fs import EMPTY_DIGEST, AddPrefix, Digest, MergeDigests, Workspace
25 from pants.engine.goal import Goal, GoalSubsystem
26 from pants.engine.internals.selectors import Effect, Get, MultiGet
27 from pants.engine.process import InteractiveProcess, InteractiveProcessResult
28 from pants.engine.rules import collect_rules, goal_rule
29 from pants.engine.target import FilteredTargets, Target
30 from pants.engine.unions import UnionMembership, union
31 from pants.option.option_types import StrListOption
32 from pants.util.dirutil import safe_rmtree
33 from pants.util.frozendict import FrozenDict
34 from pants.util.strutil import softwrap
35 
36 
37 class ExportError(Exception):
38     pass
39 
40 
41 @union(in_scope_types=[EnvironmentName])
42 @dataclass(frozen=True)
43 class ExportRequest:
44     """A union for exportable data provided by a backend.
45 
46     Subclass and install a member of this type to export data.
47     """
48 
49     targets: Sequence[Target]
50 
51 
52 @dataclass(frozen=True)
53 class PostProcessingCommand:
54     """A command to run as a local process after an exported digest is materialized."""
55 
56     # Values in the argv tuple can contain the format specifier "{digest_root}", which will be
57     # substituted with the (absolute) path to the location under distdir in which the
58     # digest is materialized.
59     argv: tuple[str, ...]
60     # The command will be run with an environment consisting of just PATH, set to the Pants
61     # process's own PATH env var, plus these extra env vars.
62     extra_env: FrozenDict[str, str]
63 
64     def __init__(
65         self,
66         argv: Iterable[str],
67         extra_env: Mapping[str, str] = FrozenDict(),
68     ):
69         object.__setattr__(self, "argv", tuple(argv))
70         object.__setattr__(self, "extra_env", FrozenDict(extra_env))
71 
72 
73 @dataclass(frozen=True)
74 class ExportResult:
75     description: str
76     # Materialize digests under this reldir.
77     reldir: str
78     # Materialize this digest.
79     digest: Digest
80     # Run these commands as local processes after the digest is materialized.
81     post_processing_cmds: tuple[PostProcessingCommand, ...]
82     # Set for the common special case of exporting a resolve, and names that resolve.
83     # Set to None for other export results.
84     resolve: str | None
85 
86     def __init__(
87         self,
88         description: str,
89         reldir: str,
90         *,
91         digest: Digest = EMPTY_DIGEST,
92         post_processing_cmds: Iterable[PostProcessingCommand] = tuple(),
93         resolve: str | None = None,
94     ):
95         object.__setattr__(self, "description", description)
96         object.__setattr__(self, "reldir", reldir)
97         object.__setattr__(self, "digest", digest)
98         object.__setattr__(self, "post_processing_cmds", tuple(post_processing_cmds))
99         object.__setattr__(self, "resolve", resolve)
100 
101 
102 class ExportResults(Collection[ExportResult]):
103     pass
104 
105 
106 class ExportSubsystem(GoalSubsystem):
107     name = "export"
108     help = softwrap(
109         """
110         Export Pants data for use in other tools, such as IDEs.
111 
112         :::caution Exporting tools requires creating a custom lockfile for them
113         Follow [the instructions for creating tool lockfiles](../../docs/python/overview/lockfiles#lockfiles-for-tools)
114         :::
115         """
116     )
117 
118     # NB: Only options that are relevant across many/most backends and languages
119     #  should be defined here.  Backend-specific options should be defined in that backend
120     #  as plugin options on this subsystem.
121 
122     # Exporting resolves is a common use-case for `export`, often the primary one, so we
123     # add affordances for it at the core goal level.
124     resolve = StrListOption(
125         default=[],
126         help="Export the specified resolve(s). The export format is backend-specific, "
127         "e.g., Python resolves are exported as virtualenvs.",
128     )
129 
130 
131 class Export(Goal):
132     subsystem_cls = ExportSubsystem
133     environment_behavior = Goal.EnvironmentBehavior.LOCAL_ONLY
134 
135 
136 @goal_rule
137 async def export(
138     console: Console,
139     targets: FilteredTargets,
140     workspace: Workspace,
141     union_membership: UnionMembership,
142     build_root: BuildRoot,
143     dist_dir: DistDir,
144     export_subsys: ExportSubsystem,
145 ) -> Export:
146     request_types = cast("Iterable[type[ExportRequest]]", union_membership.get(ExportRequest))
147     requests = tuple(request_type(targets) for request_type in request_types)
148     all_results = await MultiGet(Get(ExportResults, ExportRequest, request) for request in requests)
149     flattened_results = [res for results in all_results for res in results]
150 
151     await _warn_on_non_local_environments(targets, "the `export` goal")
152 
153     prefixed_digests = await MultiGet(
154         Get(Digest, AddPrefix(result.digest, result.reldir)) for result in flattened_results
155     )
156     output_dir = os.path.join(str(dist_dir.relpath), "export")
157     for result in flattened_results:
158         digest_root = os.path.join(build_root.path, output_dir, result.reldir)
159         safe_rmtree(digest_root)
160     merged_digest = await Get(Digest, MergeDigests(prefixed_digests))
161     dist_digest = await Get(Digest, AddPrefix(merged_digest, output_dir))
162     workspace.write_digest(dist_digest)
163     environment = await Get(EnvironmentVars, EnvironmentVarsRequest(["PATH"]))
164     resolves_exported = set()
165     for result in flattened_results:
166         result_dir = os.path.join(output_dir, result.reldir)
167         digest_root = os.path.join(build_root.path, result_dir)
168         for cmd in result.post_processing_cmds:
169             argv = tuple(arg.format(digest_root=digest_root) for arg in cmd.argv)
170             ip = InteractiveProcess(
171                 argv=argv,
172                 env={"PATH": environment.get("PATH", ""), **cmd.extra_env},
173                 run_in_workspace=True,
174             )
175             ipr = await Effect(InteractiveProcessResult, InteractiveProcess, ip)
176             if ipr.exit_code:
177                 raise ExportError(f"Failed to write {result.description} to {result_dir}")
178         if result.resolve:
179             resolves_exported.add(result.resolve)
180         console.print_stdout(f"Wrote {result.description} to {result_dir}")
181 
182     unexported_resolves = sorted(set(export_subsys.resolve) - resolves_exported)
183     if unexported_resolves:
184         all_known_user_resolve_names = await MultiGet(
185             Get(KnownUserResolveNames, KnownUserResolveNamesRequest, request())
186             for request in union_membership.get(KnownUserResolveNamesRequest)
187         )
188         all_valid_resolve_names = sorted(
189             {
190                 *itertools.chain.from_iterable(kurn.names for kurn in all_known_user_resolve_names),
191                 *(
192                     sentinel.resolve_name
193                     for sentinel in union_membership.get(GenerateToolLockfileSentinel)
194                 ),
195             }
196         )
197         raise UnrecognizedResolveNamesError(
198             unexported_resolves,
199             all_valid_resolve_names,
200             description_of_origin="the option --export-resolve",
201         )
202 
203     return Export(exit_code=0)
204 
205 
206 def rules():
207     return collect_rules()
208
```
--- 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/core/goals/export.py b/src/python/pants/core/goals/export.py
--- a/src/python/pants/core/goals/export.py
+++ b/src/python/pants/core/goals/export.py
@@ -110,7 +110,9 @@
         Export Pants data for use in other tools, such as IDEs.
 
         :::caution Exporting tools requires creating a custom lockfile for them
+
         Follow [the instructions for creating tool lockfiles](../../docs/python/overview/lockfiles#lockfiles-for-tools)
+
         :::
         """
     )

{"golden_diff": "diff --git a/src/python/pants/core/goals/export.py b/src/python/pants/core/goals/export.py\n--- a/src/python/pants/core/goals/export.py\n+++ b/src/python/pants/core/goals/export.py\n@@ -110,7 +110,9 @@\n         Export Pants data for use in other tools, such as IDEs.\n \n         :::caution Exporting tools requires creating a custom lockfile for them\n+\n         Follow [the instructions for creating tool lockfiles](../../docs/python/overview/lockfiles#lockfiles-for-tools)\n+\n         :::\n         \"\"\"\n     )\n", "issue": "\"export\" goal docs have unclosed call out\n**Describe the bug**\r\n\r\nThe warning callout about \"exporting tools requires ...\" seems to be unclosed:\r\n\r\n- https://www.pantsbuild.org/2.18/reference/goals/export\r\n- https://www.pantsbuild.org/2.19/reference/goals/export\r\n- https://www.pantsbuild.org/2.20/reference/goals/export\r\n\r\n![image](https://github.com/pantsbuild/pants/assets/1203825/5cfc04ee-27a7-49ba-a695-80d601aeace6)\r\n\r\n**Pants version**\r\n2.18 onwards\r\n\r\n**OS**\r\nmacOS\r\n\r\n**Additional info**\r\nIntroduced in #20604 \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 itertools\nimport os\nfrom dataclasses import dataclass\nfrom typing import Iterable, Mapping, Sequence, cast\n\nfrom pants.base.build_root import BuildRoot\nfrom pants.core.goals.generate_lockfiles import (\n    GenerateToolLockfileSentinel,\n    KnownUserResolveNames,\n    KnownUserResolveNamesRequest,\n    UnrecognizedResolveNamesError,\n)\nfrom pants.core.util_rules.distdir import DistDir\nfrom pants.core.util_rules.environments import _warn_on_non_local_environments\nfrom pants.engine.collection import Collection\nfrom pants.engine.console import Console\nfrom pants.engine.env_vars import EnvironmentVars, EnvironmentVarsRequest\nfrom pants.engine.environment import EnvironmentName\nfrom pants.engine.fs import EMPTY_DIGEST, AddPrefix, Digest, MergeDigests, Workspace\nfrom pants.engine.goal import Goal, GoalSubsystem\nfrom pants.engine.internals.selectors import Effect, Get, MultiGet\nfrom pants.engine.process import InteractiveProcess, InteractiveProcessResult\nfrom pants.engine.rules import collect_rules, goal_rule\nfrom pants.engine.target import FilteredTargets, Target\nfrom pants.engine.unions import UnionMembership, union\nfrom pants.option.option_types import StrListOption\nfrom pants.util.dirutil import safe_rmtree\nfrom pants.util.frozendict import FrozenDict\nfrom pants.util.strutil import softwrap\n\n\nclass ExportError(Exception):\n    pass\n\n\n@union(in_scope_types=[EnvironmentName])\n@dataclass(frozen=True)\nclass ExportRequest:\n    \"\"\"A union for exportable data provided by a backend.\n\n    Subclass and install a member of this type to export data.\n    \"\"\"\n\n    targets: Sequence[Target]\n\n\n@dataclass(frozen=True)\nclass PostProcessingCommand:\n    \"\"\"A command to run as a local process after an exported digest is materialized.\"\"\"\n\n    # Values in the argv tuple can contain the format specifier \"{digest_root}\", which will be\n    # substituted with the (absolute) path to the location under distdir in which the\n    # digest is materialized.\n    argv: tuple[str, ...]\n    # The command will be run with an environment consisting of just PATH, set to the Pants\n    # process's own PATH env var, plus these extra env vars.\n    extra_env: FrozenDict[str, str]\n\n    def __init__(\n        self,\n        argv: Iterable[str],\n        extra_env: Mapping[str, str] = FrozenDict(),\n    ):\n        object.__setattr__(self, \"argv\", tuple(argv))\n        object.__setattr__(self, \"extra_env\", FrozenDict(extra_env))\n\n\n@dataclass(frozen=True)\nclass ExportResult:\n    description: str\n    # Materialize digests under this reldir.\n    reldir: str\n    # Materialize this digest.\n    digest: Digest\n    # Run these commands as local processes after the digest is materialized.\n    post_processing_cmds: tuple[PostProcessingCommand, ...]\n    # Set for the common special case of exporting a resolve, and names that resolve.\n    # Set to None for other export results.\n    resolve: str | None\n\n    def __init__(\n        self,\n        description: str,\n        reldir: str,\n        *,\n        digest: Digest = EMPTY_DIGEST,\n        post_processing_cmds: Iterable[PostProcessingCommand] = tuple(),\n        resolve: str | None = None,\n    ):\n        object.__setattr__(self, \"description\", description)\n        object.__setattr__(self, \"reldir\", reldir)\n        object.__setattr__(self, \"digest\", digest)\n        object.__setattr__(self, \"post_processing_cmds\", tuple(post_processing_cmds))\n        object.__setattr__(self, \"resolve\", resolve)\n\n\nclass ExportResults(Collection[ExportResult]):\n    pass\n\n\nclass ExportSubsystem(GoalSubsystem):\n    name = \"export\"\n    help = softwrap(\n        \"\"\"\n        Export Pants data for use in other tools, such as IDEs.\n\n        :::caution Exporting tools requires creating a custom lockfile for them\n        Follow [the instructions for creating tool lockfiles](../../docs/python/overview/lockfiles#lockfiles-for-tools)\n        :::\n        \"\"\"\n    )\n\n    # NB: Only options that are relevant across many/most backends and languages\n    #  should be defined here.  Backend-specific options should be defined in that backend\n    #  as plugin options on this subsystem.\n\n    # Exporting resolves is a common use-case for `export`, often the primary one, so we\n    # add affordances for it at the core goal level.\n    resolve = StrListOption(\n        default=[],\n        help=\"Export the specified resolve(s). The export format is backend-specific, \"\n        \"e.g., Python resolves are exported as virtualenvs.\",\n    )\n\n\nclass Export(Goal):\n    subsystem_cls = ExportSubsystem\n    environment_behavior = Goal.EnvironmentBehavior.LOCAL_ONLY\n\n\n@goal_rule\nasync def export(\n    console: Console,\n    targets: FilteredTargets,\n    workspace: Workspace,\n    union_membership: UnionMembership,\n    build_root: BuildRoot,\n    dist_dir: DistDir,\n    export_subsys: ExportSubsystem,\n) -> Export:\n    request_types = cast(\"Iterable[type[ExportRequest]]\", union_membership.get(ExportRequest))\n    requests = tuple(request_type(targets) for request_type in request_types)\n    all_results = await MultiGet(Get(ExportResults, ExportRequest, request) for request in requests)\n    flattened_results = [res for results in all_results for res in results]\n\n    await _warn_on_non_local_environments(targets, \"the `export` goal\")\n\n    prefixed_digests = await MultiGet(\n        Get(Digest, AddPrefix(result.digest, result.reldir)) for result in flattened_results\n    )\n    output_dir = os.path.join(str(dist_dir.relpath), \"export\")\n    for result in flattened_results:\n        digest_root = os.path.join(build_root.path, output_dir, result.reldir)\n        safe_rmtree(digest_root)\n    merged_digest = await Get(Digest, MergeDigests(prefixed_digests))\n    dist_digest = await Get(Digest, AddPrefix(merged_digest, output_dir))\n    workspace.write_digest(dist_digest)\n    environment = await Get(EnvironmentVars, EnvironmentVarsRequest([\"PATH\"]))\n    resolves_exported = set()\n    for result in flattened_results:\n        result_dir = os.path.join(output_dir, result.reldir)\n        digest_root = os.path.join(build_root.path, result_dir)\n        for cmd in result.post_processing_cmds:\n            argv = tuple(arg.format(digest_root=digest_root) for arg in cmd.argv)\n            ip = InteractiveProcess(\n                argv=argv,\n                env={\"PATH\": environment.get(\"PATH\", \"\"), **cmd.extra_env},\n                run_in_workspace=True,\n            )\n            ipr = await Effect(InteractiveProcessResult, InteractiveProcess, ip)\n            if ipr.exit_code:\n                raise ExportError(f\"Failed to write {result.description} to {result_dir}\")\n        if result.resolve:\n            resolves_exported.add(result.resolve)\n        console.print_stdout(f\"Wrote {result.description} to {result_dir}\")\n\n    unexported_resolves = sorted(set(export_subsys.resolve) - resolves_exported)\n    if unexported_resolves:\n        all_known_user_resolve_names = await MultiGet(\n            Get(KnownUserResolveNames, KnownUserResolveNamesRequest, request())\n            for request in union_membership.get(KnownUserResolveNamesRequest)\n        )\n        all_valid_resolve_names = sorted(\n            {\n                *itertools.chain.from_iterable(kurn.names for kurn in all_known_user_resolve_names),\n                *(\n                    sentinel.resolve_name\n                    for sentinel in union_membership.get(GenerateToolLockfileSentinel)\n                ),\n            }\n        )\n        raise UnrecognizedResolveNamesError(\n            unexported_resolves,\n            all_valid_resolve_names,\n            description_of_origin=\"the option --export-resolve\",\n        )\n\n    return Export(exit_code=0)\n\n\ndef rules():\n    return collect_rules()\n", "path": "src/python/pants/core/goals/export.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 itertools\nimport os\nfrom dataclasses import dataclass\nfrom typing import Iterable, Mapping, Sequence, cast\n\nfrom pants.base.build_root import BuildRoot\nfrom pants.core.goals.generate_lockfiles import (\n    GenerateToolLockfileSentinel,\n    KnownUserResolveNames,\n    KnownUserResolveNamesRequest,\n    UnrecognizedResolveNamesError,\n)\nfrom pants.core.util_rules.distdir import DistDir\nfrom pants.core.util_rules.environments import _warn_on_non_local_environments\nfrom pants.engine.collection import Collection\nfrom pants.engine.console import Console\nfrom pants.engine.env_vars import EnvironmentVars, EnvironmentVarsRequest\nfrom pants.engine.environment import EnvironmentName\nfrom pants.engine.fs import EMPTY_DIGEST, AddPrefix, Digest, MergeDigests, Workspace\nfrom pants.engine.goal import Goal, GoalSubsystem\nfrom pants.engine.internals.selectors import Effect, Get, MultiGet\nfrom pants.engine.process import InteractiveProcess, InteractiveProcessResult\nfrom pants.engine.rules import collect_rules, goal_rule\nfrom pants.engine.target import FilteredTargets, Target\nfrom pants.engine.unions import UnionMembership, union\nfrom pants.option.option_types import StrListOption\nfrom pants.util.dirutil import safe_rmtree\nfrom pants.util.frozendict import FrozenDict\nfrom pants.util.strutil import softwrap\n\n\nclass ExportError(Exception):\n    pass\n\n\n@union(in_scope_types=[EnvironmentName])\n@dataclass(frozen=True)\nclass ExportRequest:\n    \"\"\"A union for exportable data provided by a backend.\n\n    Subclass and install a member of this type to export data.\n    \"\"\"\n\n    targets: Sequence[Target]\n\n\n@dataclass(frozen=True)\nclass PostProcessingCommand:\n    \"\"\"A command to run as a local process after an exported digest is materialized.\"\"\"\n\n    # Values in the argv tuple can contain the format specifier \"{digest_root}\", which will be\n    # substituted with the (absolute) path to the location under distdir in which the\n    # digest is materialized.\n    argv: tuple[str, ...]\n    # The command will be run with an environment consisting of just PATH, set to the Pants\n    # process's own PATH env var, plus these extra env vars.\n    extra_env: FrozenDict[str, str]\n\n    def __init__(\n        self,\n        argv: Iterable[str],\n        extra_env: Mapping[str, str] = FrozenDict(),\n    ):\n        object.__setattr__(self, \"argv\", tuple(argv))\n        object.__setattr__(self, \"extra_env\", FrozenDict(extra_env))\n\n\n@dataclass(frozen=True)\nclass ExportResult:\n    description: str\n    # Materialize digests under this reldir.\n    reldir: str\n    # Materialize this digest.\n    digest: Digest\n    # Run these commands as local processes after the digest is materialized.\n    post_processing_cmds: tuple[PostProcessingCommand, ...]\n    # Set for the common special case of exporting a resolve, and names that resolve.\n    # Set to None for other export results.\n    resolve: str | None\n\n    def __init__(\n        self,\n        description: str,\n        reldir: str,\n        *,\n        digest: Digest = EMPTY_DIGEST,\n        post_processing_cmds: Iterable[PostProcessingCommand] = tuple(),\n        resolve: str | None = None,\n    ):\n        object.__setattr__(self, \"description\", description)\n        object.__setattr__(self, \"reldir\", reldir)\n        object.__setattr__(self, \"digest\", digest)\n        object.__setattr__(self, \"post_processing_cmds\", tuple(post_processing_cmds))\n        object.__setattr__(self, \"resolve\", resolve)\n\n\nclass ExportResults(Collection[ExportResult]):\n    pass\n\n\nclass ExportSubsystem(GoalSubsystem):\n    name = \"export\"\n    help = softwrap(\n        \"\"\"\n        Export Pants data for use in other tools, such as IDEs.\n\n        :::caution Exporting tools requires creating a custom lockfile for them\n\n        Follow [the instructions for creating tool lockfiles](../../docs/python/overview/lockfiles#lockfiles-for-tools)\n\n        :::\n        \"\"\"\n    )\n\n    # NB: Only options that are relevant across many/most backends and languages\n    #  should be defined here.  Backend-specific options should be defined in that backend\n    #  as plugin options on this subsystem.\n\n    # Exporting resolves is a common use-case for `export`, often the primary one, so we\n    # add affordances for it at the core goal level.\n    resolve = StrListOption(\n        default=[],\n        help=\"Export the specified resolve(s). The export format is backend-specific, \"\n        \"e.g., Python resolves are exported as virtualenvs.\",\n    )\n\n\nclass Export(Goal):\n    subsystem_cls = ExportSubsystem\n    environment_behavior = Goal.EnvironmentBehavior.LOCAL_ONLY\n\n\n@goal_rule\nasync def export(\n    console: Console,\n    targets: FilteredTargets,\n    workspace: Workspace,\n    union_membership: UnionMembership,\n    build_root: BuildRoot,\n    dist_dir: DistDir,\n    export_subsys: ExportSubsystem,\n) -> Export:\n    request_types = cast(\"Iterable[type[ExportRequest]]\", union_membership.get(ExportRequest))\n    requests = tuple(request_type(targets) for request_type in request_types)\n    all_results = await MultiGet(Get(ExportResults, ExportRequest, request) for request in requests)\n    flattened_results = [res for results in all_results for res in results]\n\n    await _warn_on_non_local_environments(targets, \"the `export` goal\")\n\n    prefixed_digests = await MultiGet(\n        Get(Digest, AddPrefix(result.digest, result.reldir)) for result in flattened_results\n    )\n    output_dir = os.path.join(str(dist_dir.relpath), \"export\")\n    for result in flattened_results:\n        digest_root = os.path.join(build_root.path, output_dir, result.reldir)\n        safe_rmtree(digest_root)\n    merged_digest = await Get(Digest, MergeDigests(prefixed_digests))\n    dist_digest = await Get(Digest, AddPrefix(merged_digest, output_dir))\n    workspace.write_digest(dist_digest)\n    environment = await Get(EnvironmentVars, EnvironmentVarsRequest([\"PATH\"]))\n    resolves_exported = set()\n    for result in flattened_results:\n        result_dir = os.path.join(output_dir, result.reldir)\n        digest_root = os.path.join(build_root.path, result_dir)\n        for cmd in result.post_processing_cmds:\n            argv = tuple(arg.format(digest_root=digest_root) for arg in cmd.argv)\n            ip = InteractiveProcess(\n                argv=argv,\n                env={\"PATH\": environment.get(\"PATH\", \"\"), **cmd.extra_env},\n                run_in_workspace=True,\n            )\n            ipr = await Effect(InteractiveProcessResult, InteractiveProcess, ip)\n            if ipr.exit_code:\n                raise ExportError(f\"Failed to write {result.description} to {result_dir}\")\n        if result.resolve:\n            resolves_exported.add(result.resolve)\n        console.print_stdout(f\"Wrote {result.description} to {result_dir}\")\n\n    unexported_resolves = sorted(set(export_subsys.resolve) - resolves_exported)\n    if unexported_resolves:\n        all_known_user_resolve_names = await MultiGet(\n            Get(KnownUserResolveNames, KnownUserResolveNamesRequest, request())\n            for request in union_membership.get(KnownUserResolveNamesRequest)\n        )\n        all_valid_resolve_names = sorted(\n            {\n                *itertools.chain.from_iterable(kurn.names for kurn in all_known_user_resolve_names),\n                *(\n                    sentinel.resolve_name\n                    for sentinel in union_membership.get(GenerateToolLockfileSentinel)\n                ),\n            }\n        )\n        raise UnrecognizedResolveNamesError(\n            unexported_resolves,\n            all_valid_resolve_names,\n            description_of_origin=\"the option --export-resolve\",\n        )\n\n    return Export(exit_code=0)\n\n\ndef rules():\n    return collect_rules()\n", "path": "src/python/pants/core/goals/export.py"}]}