code: def build_anyscale_base_byod_images(tests: List[Test]) -> None:
      """
      Builds the Anyscale BYOD images for the given tests.
      """
      _download_dataplane_build_file()
      to_be_built = {}
      built = set()
      for test in tests:
          if not test.is_byod_cluster():
              continue
          to_be_built[test.get_anyscale_base_byod_image()] = test
  
      env = os.environ.copy()
      env["DOCKER_BUILDKIT"] = "1"
      start = int(time.time())
      # ray images are built on post-merge, so we can wait for them to be available
      while (
          len(built) < len(to_be_built)
          and int(time.time()) - start < BASE_IMAGE_WAIT_TIMEOUT
      ):
          for byod_image, test in to_be_built.items():
              py_version = test.get_python_version()
              if test.use_byod_ml_image():
                  byod_requirements = (
                      f"{REQUIREMENTS_ML_BYOD}_{py_version}.txt"
                  )
              else:
                  byod_requirements = (
                      f"{REQUIREMENTS_BYOD}_{py_version}.txt"
                  )
  
              if _image_exist(byod_image):
                  logger.info(f"Image {byod_image} already exists")
                  built.add(byod_image)
                  continue
              ray_image = test.get_ray_image()
              if not _image_exist(ray_image):
                  # TODO(can): instead of waiting for the base image to be built, we can
                  #  build it ourselves
                  timeout = BASE_IMAGE_WAIT_TIMEOUT - (
                      int(time.time()) - start
                  )
                  logger.info(
                      f"Image {ray_image} does not exist yet. "
                      f"Wait for another {timeout}s..."
                  )
                  time.sleep(BASE_IMAGE_WAIT_DURATION)
                  continue
              logger.info(f"Building {byod_image} from {ray_image}")
              with open(DATAPLANE_FILENAME, "rb") as build_file:
                  subprocess.check_call(
                      [
                          "docker",
                          "build",
                          "--build-arg",
                          f"BASE_IMAGE={ray_image}",
                          "-t",
                          byod_image,
                          "-",
                      ],
                      stdin=build_file,
                      stdout=sys.stderr,
                      env=env,
                  )
                  subprocess.check_call(
                      [
                          "docker",
                          "build",
                          "--build-arg",
                          f"BASE_IMAGE={byod_image}",
                          "--build-arg",
                          f"PIP_REQUIREMENTS={byod_requirements}",
                          "--build-arg",
                          "DEBIAN_REQUIREMENTS=requirements_debian_byod.txt",
                          "-t",
                          byod_image,
                          "-f",
                          os.path.join(
                              RELEASE_BYOD_DIR, "byod.Dockerfile"
                          ),
                          RELEASE_BYOD_DIR,
                      ],
                      stdout=sys.stderr,
                      env=env,
                  )
                  _validate_and_push(byod_image)
                  built.add(byod_image)
  
file_path: ray/release/ray_release/byod/build.py
imports: from typing import List, Optional, Dict
  import boto3
  import hashlib
  import os
  import subprocess
  import sys
  import time
  from ray_release.config import RELEASE_PACKAGE_DIR
  from ray_release.configs.global_config import get_global_config
  from ray_release.logger import logger
  from ray_release.test import Test, DATAPLANE_ECR_REPO, DATAPLANE_ECR_ML_REPO
label_desc: Use image node_id 3 for calling a global function with example usage: build_anyscale_base_byod_images(tests) without return types