Repository: pantsbuild/pants

Issue:
New `experimental-deploy` runs N^2 publish processes
**Describe the bug**
The new `experimental-deploy` goal attempts to publish those dependencies that need to be externally available before trying to make a deployment (i.e. Docker images in the case of Helm deployments).

However it does this way too many times, in particular, if the total amount of dependencies that need to be published is N, the goal will run N^2 publish processes.

**Pants version**
2.14.0.dev6

**OS**
both

**Additional info**
N/A



Discussion:

Resolving patch:
diff --git a/src/python/pants/core/goals/deploy.py b/src/python/pants/core/goals/deploy.py
--- a/src/python/pants/core/goals/deploy.py
+++ b/src/python/pants/core/goals/deploy.py
@@ -14,7 +14,7 @@
 from pants.engine.console import Console
 from pants.engine.goal import Goal, GoalSubsystem
 from pants.engine.process import InteractiveProcess, InteractiveProcessResult
-from pants.engine.rules import Effect, Get, MultiGet, collect_rules, goal_rule, rule_helper
+from pants.engine.rules import Effect, Get, MultiGet, collect_rules, goal_rule, rule, rule_helper
 from pants.engine.target import (
     FieldSet,
     FieldSetsPerTarget,
@@ -25,6 +25,7 @@
     TargetRootsToFieldSetsRequest,
 )
 from pants.engine.unions import union
+from pants.util.strutil import pluralize
 
 logger = logging.getLogger(__name__)
 
@@ -89,11 +90,18 @@ class Deploy(Goal):
     subsystem_cls = DeploySubsystem
 
 
-@rule_helper
-async def _find_publish_processes(targets: Iterable[Target]) -> PublishProcesses:
+@dataclass(frozen=True)
+class _PublishProcessesForTargetRequest:
+    target: Target
+
+
+@rule
+async def publish_process_for_target(
+    request: _PublishProcessesForTargetRequest,
+) -> PublishProcesses:
     package_field_sets, publish_field_sets = await MultiGet(
-        Get(FieldSetsPerTarget, FieldSetsPerTargetRequest(PackageFieldSet, targets)),
-        Get(FieldSetsPerTarget, FieldSetsPerTargetRequest(PublishFieldSet, targets)),
+        Get(FieldSetsPerTarget, FieldSetsPerTargetRequest(PackageFieldSet, [request.target])),
+        Get(FieldSetsPerTarget, FieldSetsPerTargetRequest(PublishFieldSet, [request.target])),
     )
 
     return await Get(
@@ -105,6 +113,15 @@ async def _find_publish_processes(targets: Iterable[Target]) -> PublishProcesses
     )
 
 
+@rule_helper
+async def _all_publish_processes(targets: Iterable[Target]) -> PublishProcesses:
+    processes_per_target = await MultiGet(
+        Get(PublishProcesses, _PublishProcessesForTargetRequest(target)) for target in targets
+    )
+
+    return PublishProcesses(chain.from_iterable(processes_per_target))
+
+
 @rule_helper
 async def _invoke_process(
     console: Console,
@@ -164,13 +181,14 @@ async def run_deploy(console: Console, deploy_subsystem: DeploySubsystem) -> Dep
     publish_targets = set(
         chain.from_iterable([deploy.publish_dependencies for deploy in deploy_processes])
     )
-    publish_processes = await _find_publish_processes(publish_targets)
+    logger.debug(f"Found {pluralize(len(publish_targets), 'dependency')}")
+    publish_processes = await _all_publish_processes(publish_targets)
 
     exit_code: int = 0
     results: list[str] = []
 
     if publish_processes:
-        logger.info("Publishing dependencies...")
+        logger.info(f"Publishing {pluralize(len(publish_processes), 'dependency')}...")
 
         # Publish all deployment dependencies first.
         for publish in publish_processes:


Test patch: