bazel: Use different RBE instances in CQ

Automatically determine the RBE instance to use in CI vs CQ. Because
this results in CQ using a dedicated instance, allow CQ jobs to write to
the cache. This should improve cache hit rates in CQ, especially for
subsequent patchsets of the same CL.

Also add a config option that allows enabling remote execution in CI and
CQ. (Although no repositories define the relevant Bazel config yet.)

Bug: 312215590
Change-Id: I1be80012a693669c41181ee63ebc2c4fda5136ab
Reviewed-on: https://pigweed-review.googlesource.com/c/infra/recipes/+/237060
Reviewed-by: Rob Mohr <mohrr@google.com>
Lint: Lint 🤖 <android-build-ayeaye@system.gserviceaccount.com>
Commit-Queue: Ted Pudlik <tpudlik@google.com>
diff --git a/recipe_modules/bazel/__init__.py b/recipe_modules/bazel/__init__.py
index 8b84c4a..0b19c8e 100644
--- a/recipe_modules/bazel/__init__.py
+++ b/recipe_modules/bazel/__init__.py
@@ -24,6 +24,7 @@
     'recipe_engine/defer',
     'recipe_engine/file',
     'recipe_engine/path',
+    'recipe_engine/platform',
     'recipe_engine/properties',
     'recipe_engine/raw_io',
     'recipe_engine/step',
diff --git a/recipe_modules/bazel/api.py b/recipe_modules/bazel/api.py
index 30d849d..9926742 100644
--- a/recipe_modules/bazel/api.py
+++ b/recipe_modules/bazel/api.py
@@ -102,14 +102,46 @@
         # Don't limit the amount Bazel will write to stdout/stderr.
         base_args.append('--experimental_ui_max_stdouterr_bytes=-1')
 
-        if config.get('remote_cache'):
-            base_args.append('--config=remote_cache')
-        if config.get('upload_local_results'):
-            if self.api.buildbucket_util.is_tryjob:
+        if config.get('remote'):
+            # TODO: b/368128573 - Support remote execution on MacOS.
+            if self.api.platform.is_linux:
+                base_args.append('--config=remote')
+            else:
                 self.api.step.empty(
-                    'ignoring upload_local_results because this is a tryjob'
+                    'ignoring remote because not running on Linux'
                 )
-            elif not config.get('remote_cache'):
+
+        elif config.get('remote_cache'):
+            # --config=remote already implies --config=remote_cache.
+            base_args.append('--config=remote_cache')
+
+        if self.api.buildbucket.build.builder.project == 'pigweed':
+            instance_name = 'pigweed-rbe-open'
+        else:
+            instance_name = 'pigweed-rbe-private'
+
+        if self.api.buildbucket_util.is_tryjob:
+            instance_name += '-pre'
+
+        base_args.append(f'--bes_instance_name={instance_name}')
+
+        if instance_name == 'pigweed-rbe-open':
+            # Ted messed up and gave the pigweed-rbe-open RBE instance a
+            # different name (default-instance instead of default_instance).
+            # Sadly this is annoying to fix because instances cannot be renamed,
+            # and you can't have more than one instance in a GCP region.
+            #
+            # TODO: b/312215590 - Fix this.
+            base_args.append(
+                '--remote_instance_name=projects/pigweed-rbe-open/instances/default-instance'
+            )
+        else:
+            base_args.append(
+                f'--remote_instance_name=projects/{instance_name}/instances/default_instance'
+            )
+
+        if config.get('upload_local_results'):
+            if not config.get('remote_cache'):
                 self.api.step.empty(
                     'ignoring upload_local_results since remote_cache is False'
                 )
@@ -121,8 +153,6 @@
         if self.continue_after_build_error:
             base_args.append('--keep_going')
 
-        success = True
-
         with (
             self.api.context(cwd=self.checkout.root),
             self.api.defer.context() as defer,
diff --git a/recipe_modules/bazel/test_api.py b/recipe_modules/bazel/test_api.py
index af52284..0ab5517 100644
--- a/recipe_modules/bazel/test_api.py
+++ b/recipe_modules/bazel/test_api.py
@@ -127,6 +127,7 @@
         *,
         name: str = 'pigweed.json',
         programs: dict[str, list[list[str]]] = None,
+        remote: bool = False,
         remote_cache: bool = True,
         upload_local_results: bool = True,
     ):
@@ -144,9 +145,10 @@
                 {
                     'pw': {
                         'bazel_presubmit': {
+                            'programs': programs,
+                            'remote': remote,
                             'remote_cache': remote_cache,
                             'upload_local_results': upload_local_results,
-                            'programs': programs,
                         },
                     },
                 },
diff --git a/recipe_modules/bazel/tests/full.py b/recipe_modules/bazel/tests/full.py
index 8be8186..da8c964 100644
--- a/recipe_modules/bazel/tests/full.py
+++ b/recipe_modules/bazel/tests/full.py
@@ -30,6 +30,7 @@
     'pigweed/bazel',
     'recipe_engine/buildbucket',
     'recipe_engine/path',
+    'recipe_engine/platform',
     'recipe_engine/properties',
 ]
 
@@ -106,19 +107,6 @@
     )
 
     yield api.test(
-        'upload-but-tryjob',
-        api.buildbucket.try_build(),
-        api.bazel.properties(),
-        api.bazel.config(remote_cache=True, upload_local_results=True),
-        api.post_process(post_process.MustRun, 'ensure bazelisk'),
-        api.post_process(
-            post_process.MustRun,
-            'ignoring upload_local_results because this is a tryjob',
-        ),
-        api.post_process(post_process.DropExpectation),
-    )
-
-    yield api.test(
         'continue-after-build-error',
         api.buildbucket.try_build(),
         api.bazel.properties(),
@@ -137,6 +125,67 @@
     )
 
     yield api.test(
+        'remote',
+        api.platform.name('linux'),
+        api.buildbucket.ci_build(),
+        api.bazel.properties(),
+        api.bazel.config(remote=True),
+        api.post_process(post_process.MustRun, 'ensure bazelisk'),
+        api.post_process(
+            post_process.StepCommandContains,
+            'bazel build //...',
+            ['--config=remote'],
+        ),
+        api.post_process(post_process.DropExpectation),
+    )
+
+    yield api.test(
+        'remote-ignored-on-mac',
+        api.platform.name('mac'),
+        api.buildbucket.ci_build(),
+        api.bazel.properties(),
+        api.bazel.config(remote=True),
+        api.post_process(post_process.MustRun, 'ensure bazelisk'),
+        api.post_process(
+            post_process.MustRun,
+            'ignoring remote because not running on Linux',
+        ),
+        api.post_process(post_process.DropExpectation),
+    )
+
+    yield api.test(
+        'instance-name-open-ci',
+        api.buildbucket.ci_build(project='pigweed'),
+        api.bazel.properties(),
+        api.bazel.config(remote_cache=True),
+        api.post_process(
+            post_process.StepCommandContains,
+            'bazel build //...',
+            [
+                '--bes_instance_name=pigweed-rbe-open',
+                '--remote_instance_name=projects/pigweed-rbe-open/instances/default-instance',
+            ],
+        ),
+        api.post_process(post_process.DropExpectation),
+    )
+
+    yield api.test(
+        'instance-name-private-cq',
+        api.buildbucket.try_build(),
+        api.bazel.properties(),
+        api.bazel.config(remote_cache=True),
+        api.post_process(
+            post_process.StepCommandContains,
+            'bazel build //...',
+            [
+                '--bes_instance_name=pigweed-rbe-private-pre',
+                '--remote_instance_name=projects/pigweed-rbe-private-pre/instances/default_instance',
+            ],
+        ),
+        api.post_process(post_process.DropExpectation),
+    )
+
+    yield api.test(
         'upload-but-no-cache',
         api.buildbucket.ci_build(),
         api.bazel.properties(),
@@ -157,10 +206,6 @@
         api.post_process(post_process.MustRun, 'ensure bazelisk'),
         api.post_process(
             post_process.DoesNotRun,
-            'ignoring upload_local_results because this is a tryjob',
-        ),
-        api.post_process(
-            post_process.DoesNotRun,
             'ignoring upload_local_results since remote_cache is False',
         ),
         api.post_process(post_process.DropExpectation),
diff --git a/recipes/bazel.expected/simple.json b/recipes/bazel.expected/simple.json
index 722217a..14e2a8f 100644
--- a/recipes/bazel.expected/simple.json
+++ b/recipes/bazel.expected/simple.json
@@ -1141,6 +1141,8 @@
       "//...",
       "--experimental_ui_max_stdouterr_bytes=-1",
       "--config=remote_cache",
+      "--bes_instance_name=pigweed-rbe-private",
+      "--remote_instance_name=projects/pigweed-rbe-private/instances/default_instance",
       "--remote_upload_local_results=true"
     ],
     "cwd": "[START_DIR]/co",
@@ -1173,6 +1175,8 @@
       "//...",
       "--experimental_ui_max_stdouterr_bytes=-1",
       "--config=remote_cache",
+      "--bes_instance_name=pigweed-rbe-private",
+      "--remote_instance_name=projects/pigweed-rbe-private/instances/default_instance",
       "--remote_upload_local_results=true"
     ],
     "cwd": "[START_DIR]/co",
@@ -1217,6 +1221,8 @@
       "//...",
       "--experimental_ui_max_stdouterr_bytes=-1",
       "--config=remote_cache",
+      "--bes_instance_name=pigweed-rbe-private",
+      "--remote_instance_name=projects/pigweed-rbe-private/instances/default_instance",
       "--remote_upload_local_results=true"
     ],
     "cwd": "[START_DIR]/co",
@@ -1308,6 +1314,8 @@
       "//...",
       "--experimental_ui_max_stdouterr_bytes=-1",
       "--config=remote_cache",
+      "--bes_instance_name=pigweed-rbe-private",
+      "--remote_instance_name=projects/pigweed-rbe-private/instances/default_instance",
       "--remote_upload_local_results=true"
     ],
     "cwd": "[START_DIR]/co",