1# -*- coding: utf-8 -*- 2# Copyright 2025 The ChromiumOS Authors 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6"""Recipe for building a Baguette rootfs image.""" 7 8import re 9import pathlib 10from typing import Generator 11 12from PB.recipes.crosvm.build_baguette_image import BuildBaguetteImageProperties 13from recipe_engine import post_process 14from recipe_engine.recipe_api import RecipeApi 15from recipe_engine.recipe_api import StepFailure 16from recipe_engine.recipe_test_api import RecipeTestApi 17from recipe_engine.recipe_test_api import TestData 18 19DEPS = [ 20 "recipe_engine/buildbucket", 21 "recipe_engine/context", 22 "recipe_engine/file", 23 "recipe_engine/path", 24 "recipe_engine/properties", 25 "recipe_engine/step", 26 "recipe_engine/time", 27 "recipe_engine/raw_io", 28 "depot_tools/depot_tools", 29 "depot_tools/gclient", 30 "depot_tools/bot_update", 31 "depot_tools/gsutil", 32 "depot_tools/git", 33] 34 35PROPERTIES = BuildBaguetteImageProperties 36 37_GCP_PREFIX = "https://storage.googleapis.com" 38_PLATFORM2_REPO_URL = "https://chromium.googlesource.com/chromiumos/platform2/" 39_BAGUETTE_CODE_PATH = "vm_tools/baguette_image" 40_GCP_BUCKET = "cros-containers" 41_GCP_BUCKET_PATH = "baguette/images/" 42 43_AMD64_IMAGE_BUILD_PATH = "docker_export/baguette_rootfs_amd64.img.zstd" 44_ARM64_IMAGE_BUILD_PATH = "docker_export/baguette_rootfs_arm64.img.zstd" 45 46# image file name should be "baguette_rootfs_<arch>_<build time>_<commit hash>.img.zstd" 47 48 49def RunSteps(api: RecipeApi, properties: BuildBaguetteImageProperties) -> None: 50 with api.context(cwd=api.path.cache_dir, infra_steps=True): 51 gclient_config = api.gclient.make_config() 52 s = gclient_config.solutions.add() 53 s.url = _PLATFORM2_REPO_URL 54 s.name = "platform2" 55 api.bot_update.ensure_checkout(gclient_config=gclient_config) 56 with api.context(cwd=api.path.cache_dir / "platform2" / _BAGUETTE_CODE_PATH): 57 58 api.step("check docker buildx install", ["docker", "buildx"]) 59 60 # Version the image. 61 version_time = api.time.utcnow().strftime("%Y-%m-%d-%H%M%S") 62 result = api.step("Get git hash", ["git", "rev-parse", "HEAD"], stdout=api.raw_io.output()) 63 commit_hash = result.stdout.strip().decode("utf-8") 64 65 archive_name_amd64 = f"baguette_rootfs_amd64_{version_time}_{commit_hash}.img.zstd" 66 archive_name_arm64 = f"baguette_rootfs_arm64_{version_time}_{commit_hash}.img.zstd" 67 api.step("build baguette images", ["./src/docker-build.sh"]) 68 69 with api.step.nest("upload VM images") as presentation: 70 if properties.destination_gs_bucket: 71 bucket_name = properties.destination_gs_bucket 72 else: 73 bucket_name = _GCP_BUCKET 74 if properties.destination_gs_path: 75 path_name = properties.destination_gs_path 76 else: 77 path_name = _GCP_BUCKET_PATH 78 79 with api.step.nest("upload amd64 image") as presentation: 80 amd64_path_name = str(pathlib.Path(path_name) / archive_name_amd64) 81 api.gsutil.upload(_AMD64_IMAGE_BUILD_PATH, bucket_name, amd64_path_name) 82 presentation.links["image"] = api.path.join( 83 _GCP_PREFIX, bucket_name, amd64_path_name 84 ) 85 with api.step.nest("upload arm64 image") as presentation: 86 arm64_path_name = str(pathlib.Path(path_name) / archive_name_arm64) 87 api.gsutil.upload(_ARM64_IMAGE_BUILD_PATH, bucket_name, arm64_path_name) 88 presentation.links["image"] = api.path.join( 89 _GCP_PREFIX, bucket_name, arm64_path_name 90 ) 91 92 93def GenTests(api: RecipeTestApi) -> Generator[TestData, None, None]: 94 good_props = { 95 "destination_gs_bucket": "cros-containers-staging", 96 "destination_gs_path": "baguette/images/", 97 } 98 empty_props = {} 99 100 yield api.test( 101 "full props", 102 api.properties(**good_props), 103 ) 104 105 yield api.test( 106 "no props", 107 api.properties(**empty_props), 108 ) 109