pw_presubmit: Read bazel.output.base

Read bazel.output.base and use it as the build_dir when calling
cipd_upload.manifest().

Bug: b/332357274
Change-Id: Iae666016bfd6ff901345591711428dc163bfe740
Reviewed-on: https://pigweed-review.googlesource.com/c/infra/recipes/+/206271
Pigweed-Auto-Submit: Rob Mohr <mohrr@google.com>
Commit-Queue: Auto-Submit <auto-submit@pigweed-service-accounts.iam.gserviceaccount.com>
Reviewed-by: Taylor Cramer <cramertj@google.com>
Presubmit-Verified: CQ Bot Account <pigweed-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/recipe_modules/pw_presubmit/api.py b/recipe_modules/pw_presubmit/api.py
index 92e50a8..afd5ac9 100644
--- a/recipe_modules/pw_presubmit/api.py
+++ b/recipe_modules/pw_presubmit/api.py
@@ -281,6 +281,20 @@
                 else:
                     result = defer(self._run, ctx, args, name=step.name)
 
+                base_dir = step.dir
+                bazel_output_base = step.dir / 'bazel.output.base'
+                self.m.path.mock_add_file(bazel_output_base)
+                if self.m.path.isfile(bazel_output_base):
+                    base_dir = self.m.path.abs_to_path(
+                        self.m.file.read_text(
+                            f'read {bazel_output_base.name}',
+                            bazel_output_base,
+                            test_data=str(
+                                self.m.path.tmp_base_dir / 'output-base'
+                            ),
+                        ).strip()
+                    )
+
                 builder_manifest = step.dir / 'builder_manifest.json'
                 if self.m.path.isfile(builder_manifest):
                     for cipd_manifest in self.m.file.read_json(
@@ -299,7 +313,7 @@
                             defer(
                                 self.m.cipd_upload.manifest,
                                 manifest_path=step.dir / cipd_manifest,
-                                build_dir=step.dir,
+                                build_dir=base_dir,
                                 checkout=ctx.checkout,
                                 upload_to_cipd=(
                                     not self.m.buildbucket_util.is_dev_or_try
diff --git a/recipe_modules/pw_presubmit/tests/full.expected/bad-json-steps.json b/recipe_modules/pw_presubmit/tests/full.expected/bad-json-steps.json
index 79633ed..e6fcc3f 100644
--- a/recipe_modules/pw_presubmit/tests/full.expected/bad-json-steps.json
+++ b/recipe_modules/pw_presubmit/tests/full.expected/bad-json-steps.json
@@ -1680,6 +1680,37 @@
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
       "--json-output",
       "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/checkout/p/program_0/bazel.output.base",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "program_0.read bazel.output.base",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@bazel.output.base@[TMP_BASE]/output-base@@@",
+      "@@@STEP_LOG_END@bazel.output.base@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
       "ensure-directory",
       "--mode",
       "0o777",
@@ -3069,6 +3100,37 @@
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
       "--json-output",
       "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/checkout/p/program_1/bazel.output.base",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "program_1.read bazel.output.base",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@bazel.output.base@[TMP_BASE]/output-base@@@",
+      "@@@STEP_LOG_END@bazel.output.base@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
       "ensure-directory",
       "--mode",
       "0o777",
diff --git a/recipe_modules/pw_presubmit/tests/full.expected/cipd.json b/recipe_modules/pw_presubmit/tests/full.expected/cipd.json
index 73581e5..06ce1b7 100644
--- a/recipe_modules/pw_presubmit/tests/full.expected/cipd.json
+++ b/recipe_modules/pw_presubmit/tests/full.expected/cipd.json
@@ -1668,6 +1668,37 @@
       "--json-output",
       "/path/to/tmp/json",
       "copy",
+      "[START_DIR]/checkout/p/step1/bazel.output.base",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "step1.read bazel.output.base",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@bazel.output.base@[TMP_BASE]/output-base@@@",
+      "@@@STEP_LOG_END@bazel.output.base@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
       "[START_DIR]/checkout/p/step1/builder_manifest.json",
       "/path/to/tmp/"
     ],
diff --git a/recipe_modules/pw_presubmit/tests/full.expected/comment-always-disallowed-host.json b/recipe_modules/pw_presubmit/tests/full.expected/comment-always-disallowed-host.json
index 1633bad..28cc660 100644
--- a/recipe_modules/pw_presubmit/tests/full.expected/comment-always-disallowed-host.json
+++ b/recipe_modules/pw_presubmit/tests/full.expected/comment-always-disallowed-host.json
@@ -1829,6 +1829,37 @@
     ]
   },
   {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/checkout/p/step1/bazel.output.base",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:try"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "step1.read bazel.output.base",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@bazel.output.base@[TMP_BASE]/output-base@@@",
+      "@@@STEP_LOG_END@bazel.output.base@@@"
+    ]
+  },
+  {
     "cmd": [],
     "name": "step1.maybe post",
     "~followup_annotations": [
@@ -3228,6 +3259,37 @@
     ]
   },
   {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/checkout/p/step2/bazel.output.base",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:try"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "step2.read bazel.output.base",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@bazel.output.base@[TMP_BASE]/output-base@@@",
+      "@@@STEP_LOG_END@bazel.output.base@@@"
+    ]
+  },
+  {
     "cmd": [],
     "name": "step2.maybe post",
     "~followup_annotations": [
diff --git a/recipe_modules/pw_presubmit/tests/full.expected/comment-always-no-cl.json b/recipe_modules/pw_presubmit/tests/full.expected/comment-always-no-cl.json
index 550dd5b..cbf536e 100644
--- a/recipe_modules/pw_presubmit/tests/full.expected/comment-always-no-cl.json
+++ b/recipe_modules/pw_presubmit/tests/full.expected/comment-always-no-cl.json
@@ -1666,6 +1666,37 @@
     ]
   },
   {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/checkout/p/step1/bazel.output.base",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "step1.read bazel.output.base",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@bazel.output.base@[TMP_BASE]/output-base@@@",
+      "@@@STEP_LOG_END@bazel.output.base@@@"
+    ]
+  },
+  {
     "cmd": [],
     "name": "step1.maybe post",
     "~followup_annotations": [
@@ -3065,6 +3096,37 @@
     ]
   },
   {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/checkout/p/step2/bazel.output.base",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "step2.read bazel.output.base",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@bazel.output.base@[TMP_BASE]/output-base@@@",
+      "@@@STEP_LOG_END@bazel.output.base@@@"
+    ]
+  },
+  {
     "cmd": [],
     "name": "step2.maybe post",
     "~followup_annotations": [
diff --git a/recipe_modules/pw_presubmit/tests/full.expected/comment-always.json b/recipe_modules/pw_presubmit/tests/full.expected/comment-always.json
index e56759e..be6c355 100644
--- a/recipe_modules/pw_presubmit/tests/full.expected/comment-always.json
+++ b/recipe_modules/pw_presubmit/tests/full.expected/comment-always.json
@@ -1829,6 +1829,37 @@
     ]
   },
   {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/checkout/p/step1/bazel.output.base",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:try"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "step1.read bazel.output.base",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@bazel.output.base@[TMP_BASE]/output-base@@@",
+      "@@@STEP_LOG_END@bazel.output.base@@@"
+    ]
+  },
+  {
     "cmd": [],
     "name": "step1.maybe post",
     "~followup_annotations": [
@@ -3271,6 +3302,37 @@
     ]
   },
   {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/checkout/p/step2/bazel.output.base",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:try"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "step2.read bazel.output.base",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@bazel.output.base@[TMP_BASE]/output-base@@@",
+      "@@@STEP_LOG_END@bazel.output.base@@@"
+    ]
+  },
+  {
     "cmd": [],
     "name": "step2.maybe post",
     "~followup_annotations": [
diff --git a/recipe_modules/pw_presubmit/tests/full.expected/comment-on-failure.json b/recipe_modules/pw_presubmit/tests/full.expected/comment-on-failure.json
index 02e9eb6..bca9003 100644
--- a/recipe_modules/pw_presubmit/tests/full.expected/comment-on-failure.json
+++ b/recipe_modules/pw_presubmit/tests/full.expected/comment-on-failure.json
@@ -1829,6 +1829,37 @@
     ]
   },
   {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/checkout/p/step1/bazel.output.base",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:try"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "step1.read bazel.output.base",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@bazel.output.base@[TMP_BASE]/output-base@@@",
+      "@@@STEP_LOG_END@bazel.output.base@@@"
+    ]
+  },
+  {
     "cmd": [],
     "name": "step1.maybe post",
     "~followup_annotations": [
@@ -3228,6 +3259,37 @@
     ]
   },
   {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/checkout/p/step2/bazel.output.base",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:try"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "step2.read bazel.output.base",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@bazel.output.base@[TMP_BASE]/output-base@@@",
+      "@@@STEP_LOG_END@bazel.output.base@@@"
+    ]
+  },
+  {
     "cmd": [],
     "name": "step2.maybe post",
     "~followup_annotations": [
diff --git a/recipe_modules/pw_presubmit/tests/full.expected/long.json b/recipe_modules/pw_presubmit/tests/full.expected/long.json
index 1876a2c..6a924c3 100644
--- a/recipe_modules/pw_presubmit/tests/full.expected/long.json
+++ b/recipe_modules/pw_presubmit/tests/full.expected/long.json
@@ -1835,6 +1835,37 @@
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
       "--json-output",
       "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/checkout/p/step1/bazel.output.base",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:try"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "step1.read bazel.output.base",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@bazel.output.base@[TMP_BASE]/output-base@@@",
+      "@@@STEP_LOG_END@bazel.output.base@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
       "ensure-directory",
       "--mode",
       "0o777",
@@ -3226,6 +3257,37 @@
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
       "--json-output",
       "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/checkout/p/step2/bazel.output.base",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:try"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "step2.read bazel.output.base",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@bazel.output.base@[TMP_BASE]/output-base@@@",
+      "@@@STEP_LOG_END@bazel.output.base@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
       "ensure-directory",
       "--mode",
       "0o777",
diff --git a/recipe_modules/pw_presubmit/tests/full.expected/medium.json b/recipe_modules/pw_presubmit/tests/full.expected/medium.json
index 65a34c5..dae9263 100644
--- a/recipe_modules/pw_presubmit/tests/full.expected/medium.json
+++ b/recipe_modules/pw_presubmit/tests/full.expected/medium.json
@@ -1835,6 +1835,37 @@
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
       "--json-output",
       "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/checkout/p/step1/bazel.output.base",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:try"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "step1.read bazel.output.base",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@bazel.output.base@[TMP_BASE]/output-base@@@",
+      "@@@STEP_LOG_END@bazel.output.base@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
       "ensure-directory",
       "--mode",
       "0o777",
@@ -3226,6 +3257,37 @@
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
       "--json-output",
       "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/checkout/p/step2/bazel.output.base",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:try"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "step2.read bazel.output.base",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@bazel.output.base@[TMP_BASE]/output-base@@@",
+      "@@@STEP_LOG_END@bazel.output.base@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
       "ensure-directory",
       "--mode",
       "0o777",
diff --git a/recipe_modules/pw_presubmit/tests/full.expected/pigweed.json b/recipe_modules/pw_presubmit/tests/full.expected/pigweed.json
index e85b871..e6cc0b0 100644
--- a/recipe_modules/pw_presubmit/tests/full.expected/pigweed.json
+++ b/recipe_modules/pw_presubmit/tests/full.expected/pigweed.json
@@ -1670,6 +1670,37 @@
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
       "--json-output",
       "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/checkout/p/full_0/bazel.output.base",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "full_0.read bazel.output.base",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@bazel.output.base@[TMP_BASE]/output-base@@@",
+      "@@@STEP_LOG_END@bazel.output.base@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
       "ensure-directory",
       "--mode",
       "0o777",
@@ -3061,6 +3092,37 @@
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
       "--json-output",
       "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/checkout/p/full_1/bazel.output.base",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "full_1.read bazel.output.base",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@bazel.output.base@[TMP_BASE]/output-base@@@",
+      "@@@STEP_LOG_END@bazel.output.base@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
       "ensure-directory",
       "--mode",
       "0o777",
diff --git a/recipe_modules/pw_presubmit/tests/full.expected/step.json b/recipe_modules/pw_presubmit/tests/full.expected/step.json
index 976063f..195b7e7 100644
--- a/recipe_modules/pw_presubmit/tests/full.expected/step.json
+++ b/recipe_modules/pw_presubmit/tests/full.expected/step.json
@@ -1843,6 +1843,37 @@
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
       "--json-output",
       "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/checkout/p/step1/bazel.output.base",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:try"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "step1.read bazel.output.base",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@bazel.output.base@[TMP_BASE]/output-base@@@",
+      "@@@STEP_LOG_END@bazel.output.base@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
       "ensure-directory",
       "--mode",
       "0o777",
@@ -3240,6 +3271,37 @@
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
       "--json-output",
       "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/checkout/p/step2/bazel.output.base",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:try"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "step2.read bazel.output.base",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@bazel.output.base@[TMP_BASE]/output-base@@@",
+      "@@@STEP_LOG_END@bazel.output.base@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
       "ensure-directory",
       "--mode",
       "0o777",
diff --git a/recipe_modules/pw_presubmit/tests/full.expected/substep.json b/recipe_modules/pw_presubmit/tests/full.expected/substep.json
index f1d784f..ec54c07 100644
--- a/recipe_modules/pw_presubmit/tests/full.expected/substep.json
+++ b/recipe_modules/pw_presubmit/tests/full.expected/substep.json
@@ -1127,6 +1127,25 @@
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
       "--json-output",
       "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/checkout/p/composite/bazel.output.base",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "composite.read bazel.output.base",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@bazel.output.base@[TMP_BASE]/output-base@@@",
+      "@@@STEP_LOG_END@bazel.output.base@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
       "ensure-directory",
       "--mode",
       "0o777",
diff --git a/recipes/target_to_cipd.expected/pw-presubmit.json b/recipes/target_to_cipd.expected/pw-presubmit.json
index a6ab937..8a874c4 100644
--- a/recipes/target_to_cipd.expected/pw-presubmit.json
+++ b/recipes/target_to_cipd.expected/pw-presubmit.json
@@ -1142,6 +1142,45 @@
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
       "--json-output",
       "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/co/p/step/bazel.output.base",
+      "/path/to/tmp/"
+    ],
+    "env": {
+      "BUILDBUCKET_ID": "0",
+      "BUILDBUCKET_NAME": "project:bucket:builder",
+      "BUILD_NUMBER": "0",
+      "CCACHE_DIR": "[CACHE]/ccache",
+      "CLICOLOR": "0",
+      "CLICOLOR_FORCE": "0",
+      "CTCACHE_DIR": "[CACHE]/clang_tidy",
+      "GCC_COLORS": "",
+      "GOCACHE": "[CACHE]/go",
+      "NO_COLOR": "1",
+      "PIP_CACHE_DIR": "[CACHE]/pip",
+      "PW_ENVIRONMENT_NO_ERROR_ON_UNRECOGNIZED": "1",
+      "PW_ENVSETUP_DISABLE_SPINNER": "1",
+      "PW_PRESUBMIT_DISABLE_SUBPROCESS_CAPTURE": "1",
+      "PW_TEST_VAR": "test_value",
+      "PW_USE_COLOR": "",
+      "TEST_TMPDIR": "[CACHE]/bazel",
+      "TRIGGERING_CHANGES_JSON": "[CLEANUP]/tmp_tmp_1"
+    },
+    "infra_step": true,
+    "name": "step.read bazel.output.base",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@bazel.output.base@[TMP_BASE]/output-base@@@",
+      "@@@STEP_LOG_END@bazel.output.base@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
       "ensure-directory",
       "--mode",
       "0o777",