A few improvements to bcr_presubmit.py (#2077)

Stacked on
https://github.com/bazelbuild/continuous-integration/pull/2076

- Removed `MODULE_NAME` and `MODULE_VERSION` support
- Renamed `runner` to `anonymous_module_runner` to better match
[documentation](https://github.com/bazelbuild/bazel-central-registry/blob/main/docs/README.md#anonymous-module-test)
- Support overriding Bazel version in BCR task configs.
- Support specifying concurrency for jobs (default to no concurrency
limit for BCR presubmit) .
diff --git a/buildkite/bazel-central-registry/bcr_presubmit.py b/buildkite/bazel-central-registry/bcr_presubmit.py
index b316929..b48f459 100755
--- a/buildkite/bazel-central-registry/bcr_presubmit.py
+++ b/buildkite/bazel-central-registry/bcr_presubmit.py
@@ -40,11 +40,9 @@
 
 BUILDKITE_ORG = os.environ["BUILDKITE_ORGANIZATION_SLUG"]
 
-SCRIPT_URL = {
-    "bazel-testing": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/testing/buildkite/bazel-central-registry/bcr_presubmit.py",
-    "bazel-trusted": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/buildkite/bazel-central-registry/bcr_presubmit.py",
-    "bazel": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/buildkite/bazel-central-registry/bcr_presubmit.py",
-}[BUILDKITE_ORG] + "?{}".format(int(time.time()))
+SCRIPT_URL = "https://raw.githubusercontent.com/bazelbuild/continuous-integration/{}/buildkite/bazel-central-registry/bcr_presubmit.py?{}".format(
+    bazelci.GITHUB_BRANCH, int(time.time())
+)
 
 
 def fetch_bcr_presubmit_py_command():
@@ -62,32 +60,20 @@
 
 def get_target_modules():
     """
-    If the `MODULE_NAME` and `MODULE_VERSION(S)` are specified, calculate the target modules from those env vars.
-    Otherwise, calculate target modules based on changed files from the main branch.
+    Calculate target modules based on changed files from the main branch.
     """
-    modules = []
-    if "MODULE_NAME" in os.environ:
-        name = os.environ["MODULE_NAME"]
-        if "MODULE_VERSION" in os.environ:
-            modules.append((name, os.environ["MODULE_VERSION"]))
-        elif "MODULE_VERSIONS" in os.environ:
-            for version in os.environ["MODULE_VERSIONS"].split(","):
-                modules.append((name, version))
-
-    if modules:
-        return list(set(modules))
-
     # Get the list of changed files compared to the main branch
     output = subprocess.check_output(
         ["git", "diff", "main...HEAD", "--name-only", "--pretty=format:"]
     )
+    modules = set()
     # Matching modules/<name>/<version>/
     for line in output.decode("utf-8").split():
         s = re.match(r"modules\/([^\/]+)\/([^\/]+)\/", line)
         if s:
-            modules.append(s.groups())
+            modules.add(s.groups())
 
-    return list(set(modules))
+    return sorted(modules)
 
 
 def get_metadata_json(module_name):
@@ -112,44 +98,52 @@
 def get_overlay_file(module_name, module_version, filename):
     return BCR_REPO_DIR.joinpath("modules/%s/%s/overlay/%s" % (module_name, module_version, filename))
 
-def get_task_config(module_name, module_version):
+def get_anonymous_module_task_config(module_name, module_version, bazel_version=None):
     return bazelci.load_config(http_url=None,
                                file_config=get_presubmit_yml(module_name, module_version),
-                               allow_imports=False)
+                               allow_imports=False,
+                               bazel_version=bazel_version)
 
-
-def get_test_module_task_config(module_name, module_version):
+def get_test_module_task_config(module_name, module_version, bazel_version=None):
     orig_presubmit = yaml.safe_load(open(get_presubmit_yml(module_name, module_version), "r"))
     if "bcr_test_module" in orig_presubmit:
         config = orig_presubmit["bcr_test_module"]
+        bazelci.maybe_overwrite_bazel_version(bazel_version, config)
         bazelci.expand_task_config(config)
         return config
     return {}
 
 
-def add_presubmit_jobs(module_name, module_version, task_configs, pipeline_steps, is_test_module=False):
+def add_presubmit_jobs(module_name, module_version, task_configs, pipeline_steps, is_test_module=False, overwrite_bazel_version=None, calc_concurrency=None):
     for task_name, task_config in task_configs.items():
         platform_name = bazelci.get_platform_for_task(task_name, task_config)
-        label = bazelci.PLATFORMS[platform_name]["emoji-name"] + " {0}@{1} {2}".format(
-            module_name, module_version, task_config["name"] if "name" in task_config else ""
-        )
+        platform_label = bazelci.PLATFORMS[platform_name]["emoji-name"]
+        task_name = task_config.get("name", "")
+        label = f"{module_name}@{module_version} - {platform_label} - {task_name}"
         # The bazel version should always be set in the task config due to https://github.com/bazelbuild/bazel-central-registry/pull/1387
         # But fall back to empty string for more robustness.
         bazel_version = task_config.get("bazel", "")
-        if bazel_version:
-            label = ":bazel:{} - ".format(bazel_version) + label
+        if bazel_version and not overwrite_bazel_version:
+            label = f":bazel:{bazel_version} - {label}"
         command = (
-            '%s bcr_presubmit.py %s --module_name="%s" --module_version="%s" --task=%s'
+            '%s bcr_presubmit.py %s --module_name="%s" --module_version="%s" --task=%s %s'
             % (
                 bazelci.PLATFORMS[platform_name]["python"],
-                "test_module_runner" if is_test_module else "runner",
+                "test_module_runner" if is_test_module else "anonymous_module_runner",
                 module_name,
                 module_version,
                 task_name,
+                "--overwrite_bazel_version=%s" % overwrite_bazel_version if overwrite_bazel_version else ""
             )
         )
         commands = [bazelci.fetch_bazelcipy_command(), fetch_bcr_presubmit_py_command(), command]
-        pipeline_steps.append(bazelci.create_step(label, commands, platform_name))
+        if calc_concurrency is None:
+            concurrency = concurrency_group = None
+        else:
+            queue = bazelci.PLATFORMS[platform_name].get("queue", "default")
+            concurrency = calc_concurrency(queue)
+            concurrency_group = f"bcr-presubmit-test-queue-{queue}"
+        pipeline_steps.append(bazelci.create_step(label, commands, platform_name, concurrency=concurrency, concurrency_group=concurrency_group))
 
 
 def scratch_file(root, relative_path, lines=None, mode="w"):
@@ -165,14 +159,11 @@
     return abspath
 
 
-def create_simple_repo(module_name, module_version):
-    """Create a simple Bazel module repo which depends on the target module."""
+def create_anonymous_repo(module_name, module_version):
+    """Create an anonymous Bazel module which depends on the target module."""
     root = pathlib.Path(bazelci.get_repositories_root())
     scratch_file(root, "WORKSPACE")
     scratch_file(root, "BUILD")
-    # TODO(pcloudy): Should we test this module as the root module? Maybe we do if we support dev dependency.
-    # Because if the module is not root module, dev dependencies are ignored, which can break test targets.
-    # Another work around is that we can copy the dev dependencies to the generated MODULE.bazel.
     scratch_file(root, "MODULE.bazel", ["bazel_dep(name = '%s', version = '%s')" % (module_name, module_version)])
     scratch_file(root, ".bazelrc", [
         "build --experimental_enable_bzlmod",
@@ -289,7 +280,7 @@
     return test_module_root, test_module_presubmit
 
 
-def run_test(repo_location, task_config_file, task):
+def run_test(repo_location, task_config_file, task, overwrite_bazel_version=None):
     try:
         return bazelci.main(
             [
@@ -297,7 +288,7 @@
                 "--task=" + task,
                 "--file_config=%s" % task_config_file,
                 "--repo_location=%s" % repo_location,
-            ]
+            ] + (["--overwrite_bazel_version=%s" % overwrite_bazel_version] if overwrite_bazel_version else [])
         )
     except subprocess.CalledProcessError as e:
         bazelci.eprint(str(e))
@@ -476,26 +467,30 @@
 
     subparsers.add_parser("bcr_presubmit")
 
-    runner = subparsers.add_parser("runner")
-    runner.add_argument("--module_name", type=str)
-    runner.add_argument("--module_version", type=str)
-    runner.add_argument("--task", type=str)
+    anonymous_module_runner = subparsers.add_parser("anonymous_module_runner")
+    anonymous_module_runner.add_argument("--module_name", type=str)
+    anonymous_module_runner.add_argument("--module_version", type=str)
+    anonymous_module_runner.add_argument("--overwrite_bazel_version", type=str)
+    anonymous_module_runner.add_argument("--task", type=str)
 
     test_module_runner = subparsers.add_parser("test_module_runner")
     test_module_runner.add_argument("--module_name", type=str)
     test_module_runner.add_argument("--module_version", type=str)
+    test_module_runner.add_argument("--overwrite_bazel_version", type=str)
     test_module_runner.add_argument("--task", type=str)
 
     args = parser.parse_args(argv)
+
     if args.subparsers_name == "bcr_presubmit":
         modules = get_target_modules()
         if not modules:
             bazelci.eprint("No target module versions detected in this branch!")
+
         pipeline_steps = []
         for module_name, module_version in modules:
             previous_size = len(pipeline_steps)
 
-            configs = get_task_config(module_name, module_version)
+            configs = get_anonymous_module_task_config(module_name, module_version)
             add_presubmit_jobs(module_name, module_version, configs.get("tasks", {}), pipeline_steps)
             configs = get_test_module_task_config(module_name, module_version)
             add_presubmit_jobs(module_name, module_version, configs.get("tasks", {}), pipeline_steps, is_test_module=True)
@@ -507,13 +502,13 @@
             pipeline_steps = [{"block": "Wait on BCR maintainer review", "blocked_state": "running"}] + pipeline_steps
 
         upload_jobs_to_pipeline(pipeline_steps)
-    elif args.subparsers_name == "runner":
-        repo_location = create_simple_repo(args.module_name, args.module_version)
+    elif args.subparsers_name == "anonymous_module_runner":
+        repo_location = create_anonymous_repo(args.module_name, args.module_version)
         config_file = get_presubmit_yml(args.module_name, args.module_version)
-        return run_test(repo_location, config_file, args.task)
+        return run_test(repo_location, config_file, args.task, args.overwrite_bazel_version)
     elif args.subparsers_name == "test_module_runner":
         repo_location, config_file = prepare_test_module_repo(args.module_name, args.module_version)
-        return run_test(repo_location, config_file, args.task)
+        return run_test(repo_location, config_file, args.task, args.overwrite_bazel_version)
     else:
         parser.print_help()
         return 2