bazel: Use bazelisk to get bazel

Also, add post-process checks and drop expectations for this module.

All builders using the 'bazel' recipe have explicit 'cipd_json_path'
properties set, so this should be a no-op.

Bug: b/336617748
Change-Id: If94ea446f626cf70cd763b646f66d7506898db22
Reviewed-on: https://pigweed-review.googlesource.com/c/infra/recipes/+/206154
Reviewed-by: Oliver Newman <olivernewman@google.com>
Presubmit-Verified: CQ Bot Account <pigweed-scoped@luci-project-accounts.iam.gserviceaccount.com>
Commit-Queue: Rob Mohr <mohrr@google.com>
diff --git a/recipe_modules/bazel/api.py b/recipe_modules/bazel/api.py
index cc56c56..7af9ed6 100644
--- a/recipe_modules/bazel/api.py
+++ b/recipe_modules/bazel/api.py
@@ -52,18 +52,15 @@
     api: recipe_api.RecipeApi
     checkout_root: config_types.Path
     options: Options
-    bazel: config_types.Path | None = None
+    _bazel: config_types.Path | None = None
 
-    def ensure(self) -> config_types.Path:
-        if self.bazel:
-            return self.bazel
-
+    def _ensure_cipd(self) -> config_types.Path:
         # read_json in a few lines requires absolute, normalized paths.
         json_path: config_types.Path = self.api.path.abspath(
             self.checkout_root / self.options.cipd_json_path
         )
 
-        ensure_file: self.api.cipd.EnsureFile = self.api.cipd.EnsureFile()
+        ensure_file = self.api.cipd.EnsureFile()
         for package in self.api.file.read_json(
             f'read {self.api.path.basename(json_path)}',
             json_path,
@@ -71,10 +68,32 @@
         )["packages"]:
             ensure_file.add_package(package['path'], package['tags'][0])
 
-        root: config_types.Path = self.api.path.mkdtemp()
+        root = self.api.path.mkdtemp()
         self.api.cipd.ensure(root, ensure_file, name='ensure bazel')
-        self.bazel = root / 'bazel'
-        return self.bazel
+        return root / 'bazel'
+
+    def _ensure_bazelisk(self) -> config_types.Path:
+        ensure_file = self.api.cipd.EnsureFile()
+        ensure_file.add_package(
+            'fuchsia/third_party/bazelisk/${platform}',
+            self.options.bazelisk_version or 'latest',
+        )
+
+        root = self.api.path.mkdtemp()
+        self.api.cipd.ensure(root, ensure_file, name='ensure bazelisk')
+        return root / 'bazelisk'
+
+    def ensure(self) -> config_types.Path:
+        if self._bazel:
+            return self._bazel
+
+        if self.options.cipd_json_path:
+            self._bazel = self._ensure_cipd()
+        else:
+            self._bazel = self._ensure_bazelisk()
+
+        self.api.step('bazel version', [self._bazel, 'version'])
+        return self._bazel
 
     def run(self, **kwargs) -> None:
         name: str = ' '.join(['bazel'] + list(self.options.args))
diff --git a/recipe_modules/bazel/options.proto b/recipe_modules/bazel/options.proto
index fefa3d0..d1e45a1 100644
--- a/recipe_modules/bazel/options.proto
+++ b/recipe_modules/bazel/options.proto
@@ -17,17 +17,20 @@
 
 message Options {
   // Path to a CIPD JSON file that specifies the version of Bazel to use,
-  // relative to the checkout root.
+  // relative to the checkout root. If not set Bazelisk is assumed.
   string cipd_json_path = 1;
 
+  // Whether to use Bazelisk to get Bazel. Defaults to 'latest'.
+  string bazelisk_version = 2;
+
   // Arguments to pass to bazel. Example: ['test', '...'].
   //
   // DEPRECATED. TODO(tpudlik): Remove this after migrating all users to
   // invocations.
-  repeated string args = 2;
+  repeated string args = 3;
 
   // Bazel invocations to peform.
-  repeated BazelInvocation invocations = 3;
+  repeated BazelInvocation invocations = 4;
 }
 
 message BazelInvocation {
diff --git a/recipe_modules/bazel/test_api.py b/recipe_modules/bazel/test_api.py
index ecc334e..4a9dcdd 100644
--- a/recipe_modules/bazel/test_api.py
+++ b/recipe_modules/bazel/test_api.py
@@ -25,12 +25,14 @@
     def options(
         self,
         *,
-        cipd_json_path='bazel.json',
+        bazelisk_version='',
+        cipd_json_path='',
         args=('test', '...'),
         invocations=(('build', '//...'), ('test', '//...')),
     ):
         opts = Options()
         opts.cipd_json_path = cipd_json_path
+        opts.bazelisk_version = bazelisk_version
         opts.args.extend(args)
         for invocation in invocations:
             invocation_proto = BazelInvocation(args=invocation)
diff --git a/recipe_modules/bazel/tests/full.expected/normal.json b/recipe_modules/bazel/tests/full.expected/normal.json
deleted file mode 100644
index cf76307..0000000
--- a/recipe_modules/bazel/tests/full.expected/normal.json
+++ /dev/null
@@ -1,107 +0,0 @@
-[
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "[START_DIR]/bazel.json",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "read bazel.json",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@bazel.json@{@@@",
-      "@@@STEP_LOG_LINE@bazel.json@  \"included_files\": [],@@@",
-      "@@@STEP_LOG_LINE@bazel.json@  \"packages\": [@@@",
-      "@@@STEP_LOG_LINE@bazel.json@    {@@@",
-      "@@@STEP_LOG_LINE@bazel.json@      \"path\": \"fuchsia/third_party/bazel/${platform}\",@@@",
-      "@@@STEP_LOG_LINE@bazel.json@      \"platforms\": [@@@",
-      "@@@STEP_LOG_LINE@bazel.json@        \"linux-amd64\",@@@",
-      "@@@STEP_LOG_LINE@bazel.json@        \"mac-amd64\",@@@",
-      "@@@STEP_LOG_LINE@bazel.json@        \"windows-amd64\"@@@",
-      "@@@STEP_LOG_LINE@bazel.json@      ],@@@",
-      "@@@STEP_LOG_LINE@bazel.json@      \"tags\": [@@@",
-      "@@@STEP_LOG_LINE@bazel.json@        \"version:2@6.3.0.6\"@@@",
-      "@@@STEP_LOG_LINE@bazel.json@      ],@@@",
-      "@@@STEP_LOG_LINE@bazel.json@      \"version_file\": \".versions/bazel.cipd_version\"@@@",
-      "@@@STEP_LOG_LINE@bazel.json@    },@@@",
-      "@@@STEP_LOG_LINE@bazel.json@    {@@@",
-      "@@@STEP_LOG_LINE@bazel.json@      \"path\": \"flutter/java/openjdk/${platform}\",@@@",
-      "@@@STEP_LOG_LINE@bazel.json@      \"platforms\": [@@@",
-      "@@@STEP_LOG_LINE@bazel.json@        \"linux-amd64\",@@@",
-      "@@@STEP_LOG_LINE@bazel.json@        \"mac-amd64\",@@@",
-      "@@@STEP_LOG_LINE@bazel.json@        \"mac-arm64\",@@@",
-      "@@@STEP_LOG_LINE@bazel.json@        \"windows-amd64\"@@@",
-      "@@@STEP_LOG_LINE@bazel.json@      ],@@@",
-      "@@@STEP_LOG_LINE@bazel.json@      \"tags\": [@@@",
-      "@@@STEP_LOG_LINE@bazel.json@        \"version:17\"@@@",
-      "@@@STEP_LOG_LINE@bazel.json@      ]@@@",
-      "@@@STEP_LOG_LINE@bazel.json@    }@@@",
-      "@@@STEP_LOG_LINE@bazel.json@  ]@@@",
-      "@@@STEP_LOG_LINE@bazel.json@}@@@",
-      "@@@STEP_LOG_END@bazel.json@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "cipd",
-      "ensure",
-      "-root",
-      "[CLEANUP]/tmp_tmp_1",
-      "-ensure-file",
-      "fuchsia/third_party/bazel/${platform} version:2@6.3.0.6\nflutter/java/openjdk/${platform} version:17",
-      "-max-threads",
-      "0",
-      "-json-output",
-      "/path/to/tmp/json"
-    ],
-    "name": "ensure bazel",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"\": [@@@",
-      "@@@STEP_LOG_LINE@json.output@      {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-version:17------\",@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"package\": \"flutter/java/openjdk/resolved-platform\"@@@",
-      "@@@STEP_LOG_LINE@json.output@      },@@@",
-      "@@@STEP_LOG_LINE@json.output@      {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-version:2@6.3.0.\",@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"package\": \"fuchsia/third_party/bazel/resolved-platform\"@@@",
-      "@@@STEP_LOG_LINE@json.output@      }@@@",
-      "@@@STEP_LOG_LINE@json.output@    ]@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "[CLEANUP]/tmp_tmp_1/bazel",
-      "test",
-      "..."
-    ],
-    "name": "bazel test ..."
-  },
-  {
-    "cmd": [
-      "[CLEANUP]/tmp_tmp_1/bazel",
-      "build",
-      "//..."
-    ],
-    "name": "bazel build //..."
-  },
-  {
-    "cmd": [
-      "[CLEANUP]/tmp_tmp_1/bazel",
-      "test",
-      "//..."
-    ],
-    "name": "bazel test //..."
-  },
-  {
-    "name": "$result"
-  }
-]
\ No newline at end of file
diff --git a/recipe_modules/bazel/tests/full.py b/recipe_modules/bazel/tests/full.py
index fb16e6a..59225dc 100644
--- a/recipe_modules/bazel/tests/full.py
+++ b/recipe_modules/bazel/tests/full.py
@@ -18,7 +18,7 @@
 import dataclasses
 from typing import Optional
 from PB.recipe_modules.pigweed.bazel.tests.full import InputProperties
-from recipe_engine import config_types, recipe_test_api
+from recipe_engine import config_types, post_process, recipe_test_api
 
 DEPS = [
     'pigweed/bazel',
@@ -46,4 +46,26 @@
 
 
 def GenTests(api) -> Generator[recipe_test_api.TestData, None, None]:
-    yield api.test('normal') + api.bazel.properties()
+    yield api.test(
+        'cipd',
+        api.bazel.properties(cipd_json_path='bazel.json'),
+        api.post_process(post_process.MustRun, 'ensure bazel'),
+        api.post_process(post_process.DoesNotRun, 'ensure bazelisk'),
+        api.post_process(post_process.DropExpectation),
+    )
+
+    yield api.test(
+        'bazelisk-fixed',
+        api.bazel.properties(bazelisk_version='1.19.0'),
+        api.post_process(post_process.MustRun, 'ensure bazelisk'),
+        api.post_process(post_process.DoesNotRun, 'ensure bazel'),
+        api.post_process(post_process.DropExpectation),
+    )
+
+    yield api.test(
+        'bazelisk-latest',
+        api.bazel.properties(),
+        api.post_process(post_process.MustRun, 'ensure bazelisk'),
+        api.post_process(post_process.DoesNotRun, 'ensure bazel'),
+        api.post_process(post_process.DropExpectation),
+    )
diff --git a/recipes/bazel.expected/simple.json b/recipes/bazel.expected/simple.json
index 98053cb..7beb581 100644
--- a/recipes/bazel.expected/simple.json
+++ b/recipes/bazel.expected/simple.json
@@ -959,79 +959,12 @@
   },
   {
     "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "[START_DIR]/co/bazel.json",
-      "/path/to/tmp/"
-    ],
-    "cwd": "[START_DIR]/co",
-    "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": "read bazel.json",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@bazel.json@{@@@",
-      "@@@STEP_LOG_LINE@bazel.json@  \"included_files\": [],@@@",
-      "@@@STEP_LOG_LINE@bazel.json@  \"packages\": [@@@",
-      "@@@STEP_LOG_LINE@bazel.json@    {@@@",
-      "@@@STEP_LOG_LINE@bazel.json@      \"path\": \"fuchsia/third_party/bazel/${platform}\",@@@",
-      "@@@STEP_LOG_LINE@bazel.json@      \"platforms\": [@@@",
-      "@@@STEP_LOG_LINE@bazel.json@        \"linux-amd64\",@@@",
-      "@@@STEP_LOG_LINE@bazel.json@        \"mac-amd64\",@@@",
-      "@@@STEP_LOG_LINE@bazel.json@        \"windows-amd64\"@@@",
-      "@@@STEP_LOG_LINE@bazel.json@      ],@@@",
-      "@@@STEP_LOG_LINE@bazel.json@      \"tags\": [@@@",
-      "@@@STEP_LOG_LINE@bazel.json@        \"version:2@6.3.0.6\"@@@",
-      "@@@STEP_LOG_LINE@bazel.json@      ],@@@",
-      "@@@STEP_LOG_LINE@bazel.json@      \"version_file\": \".versions/bazel.cipd_version\"@@@",
-      "@@@STEP_LOG_LINE@bazel.json@    },@@@",
-      "@@@STEP_LOG_LINE@bazel.json@    {@@@",
-      "@@@STEP_LOG_LINE@bazel.json@      \"path\": \"flutter/java/openjdk/${platform}\",@@@",
-      "@@@STEP_LOG_LINE@bazel.json@      \"platforms\": [@@@",
-      "@@@STEP_LOG_LINE@bazel.json@        \"linux-amd64\",@@@",
-      "@@@STEP_LOG_LINE@bazel.json@        \"mac-amd64\",@@@",
-      "@@@STEP_LOG_LINE@bazel.json@        \"mac-arm64\",@@@",
-      "@@@STEP_LOG_LINE@bazel.json@        \"windows-amd64\"@@@",
-      "@@@STEP_LOG_LINE@bazel.json@      ],@@@",
-      "@@@STEP_LOG_LINE@bazel.json@      \"tags\": [@@@",
-      "@@@STEP_LOG_LINE@bazel.json@        \"version:17\"@@@",
-      "@@@STEP_LOG_LINE@bazel.json@      ]@@@",
-      "@@@STEP_LOG_LINE@bazel.json@    }@@@",
-      "@@@STEP_LOG_LINE@bazel.json@  ]@@@",
-      "@@@STEP_LOG_LINE@bazel.json@}@@@",
-      "@@@STEP_LOG_END@bazel.json@@@"
-    ]
-  },
-  {
-    "cmd": [
       "cipd",
       "ensure",
       "-root",
       "[CLEANUP]/tmp_tmp_2",
       "-ensure-file",
-      "fuchsia/third_party/bazel/${platform} version:2@6.3.0.6\nflutter/java/openjdk/${platform} version:17",
+      "fuchsia/third_party/bazelisk/${platform} latest",
       "-max-threads",
       "0",
       "-json-output",
@@ -1058,18 +991,14 @@
       "TEST_TMPDIR": "[CACHE]/bazel",
       "TRIGGERING_CHANGES_JSON": "[CLEANUP]/tmp_tmp_1"
     },
-    "name": "ensure bazel",
+    "name": "ensure bazelisk",
     "~followup_annotations": [
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
       "@@@STEP_LOG_LINE@json.output@    \"\": [@@@",
       "@@@STEP_LOG_LINE@json.output@      {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-version:17------\",@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"package\": \"flutter/java/openjdk/resolved-platform\"@@@",
-      "@@@STEP_LOG_LINE@json.output@      },@@@",
-      "@@@STEP_LOG_LINE@json.output@      {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-version:2@6.3.0.\",@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"package\": \"fuchsia/third_party/bazel/resolved-platform\"@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-latest----------\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"fuchsia/third_party/bazelisk/resolved-platform\"@@@",
       "@@@STEP_LOG_LINE@json.output@      }@@@",
       "@@@STEP_LOG_LINE@json.output@    ]@@@",
       "@@@STEP_LOG_LINE@json.output@  }@@@",
@@ -1079,7 +1008,35 @@
   },
   {
     "cmd": [
-      "[CLEANUP]/tmp_tmp_2/bazel",
+      "[CLEANUP]/tmp_tmp_2/bazelisk",
+      "version"
+    ],
+    "cwd": "[START_DIR]/co",
+    "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"
+    },
+    "name": "bazel version"
+  },
+  {
+    "cmd": [
+      "[CLEANUP]/tmp_tmp_2/bazelisk",
       "test",
       "..."
     ],
@@ -1108,7 +1065,7 @@
   },
   {
     "cmd": [
-      "[CLEANUP]/tmp_tmp_2/bazel",
+      "[CLEANUP]/tmp_tmp_2/bazelisk",
       "build",
       "//..."
     ],
@@ -1137,7 +1094,7 @@
   },
   {
     "cmd": [
-      "[CLEANUP]/tmp_tmp_2/bazel",
+      "[CLEANUP]/tmp_tmp_2/bazelisk",
       "test",
       "//..."
     ],