flowey_hvlite/pipelines/
build_reproducible.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! See [`BuildReproducibleCli`]
5
6use flowey::node::prelude::FlowPlatformLinuxDistro;
7use flowey::node::prelude::ReadVar;
8use flowey::pipeline::prelude::*;
9use flowey_lib_common::git_checkout::RepoSource;
10use flowey_lib_hvlite::_jobs::build_and_publish_openhcl_igvm_from_recipe::OpenhclIgvmBuildParams;
11use flowey_lib_hvlite::build_openhcl_igvm_from_recipe::OpenhclIgvmRecipe;
12use flowey_lib_hvlite::build_openvmm_hcl::OpenvmmHclBuildProfile;
13use flowey_lib_hvlite::resolve_openhcl_kernel_package::OpenhclKernelPackageKind;
14use flowey_lib_hvlite::run_cargo_build::common::CommonArch;
15use flowey_lib_hvlite::run_cargo_build::common::CommonPlatform;
16use flowey_lib_hvlite::run_cargo_build::common::CommonTriple;
17use target_lexicon::Triple;
18
19/// A list of pre-defined OpenHCL recipes that support being built reproducibly. Each recipe has a matching CI pipeline job that can be reproduced with this local CLI.
20#[derive(clap::ValueEnum, Copy, Clone)]
21pub enum ReproducibleOpenHclRecipe {
22    X64Cvm,
23}
24
25/// Build reproducible artifacts locally. DO NOT USE IN CI.
26#[derive(clap::Args)]
27pub struct BuildReproducibleCli {
28    /// Specify which OpenHCL recipe to build / customize off-of.
29    ///
30    /// A "recipe" corresponds to the various standard IGVM SKUs that are
31    /// actively supported and tested in our build infrastructure.
32    ///
33    /// It encodes all the details of what goes into an individual IGVM file,
34    /// such as what build flags `openvmm_hcl` should be built with, what goes
35    /// into a VTL2 initrd, what `igvmfilegen` manifest is being used, etc...
36    pub recipe: ReproducibleOpenHclRecipe,
37
38    /// Build using release variants of all constituent binary components.
39    ///
40    /// Uses --profile=boot-release for openhcl_boot, --profile=openhcl-ship
41    /// when building openvmm_hcl, etc...
42    #[clap(long)]
43    pub release: bool,
44}
45
46impl IntoPipeline for BuildReproducibleCli {
47    fn into_pipeline(self, backend_hint: PipelineBackendHint) -> anyhow::Result<Pipeline> {
48        if !matches!(backend_hint, PipelineBackendHint::Local) {
49            anyhow::bail!("build-reproducible is for local use only")
50        }
51
52        let Self { recipe, release } = self;
53
54        let mut pipeline = Pipeline::new();
55
56        let (pub_openhcl_igvm, _use_openhcl_igvm) = pipeline.new_artifact("x64-cvm-openhcl-igvm");
57        let (pub_openhcl_igvm_extras, _use_openhcl_igvm_extras) =
58            pipeline.new_artifact("x64-cvm-openhcl-igvm-extras");
59
60        let cfg_common_params = crate::pipelines_shared::cfg_common_params::get_cfg_common_params(
61            &mut pipeline,
62            backend_hint,
63            None,
64        )?;
65
66        let openvmm_repo_source =
67            RepoSource::ExistingClone(ReadVar::from_static(crate::repo_root()));
68
69        let mut job = pipeline.new_job(
70            FlowPlatform::Linux(FlowPlatformLinuxDistro::Nix),
71            FlowArch::host(backend_hint),
72            "build-reproducible",
73        );
74
75        // wrap all shell commands with `nix-shell --pure --run`
76        job = job.set_command_wrapper(flowey::shell::CommandWrapperKind::NixShell {
77            path: Some(crate::repo_root().join("shell.nix")),
78        });
79
80        let openvmm_hcl_profile = if release {
81            OpenvmmHclBuildProfile::OpenvmmHclShip
82        } else {
83            OpenvmmHclBuildProfile::Debug
84        };
85
86        let (recipe_arch, kernel_kind) = match recipe {
87            ReproducibleOpenHclRecipe::X64Cvm => {
88                (CommonArch::X86_64, OpenhclKernelPackageKind::Cvm)
89            }
90        };
91
92        let igvm_file = match recipe {
93            ReproducibleOpenHclRecipe::X64Cvm => OpenhclIgvmRecipe::X64Cvm,
94        };
95
96        let openhcl_musl_target = |arch: CommonArch| -> Triple {
97            CommonTriple::Common {
98                arch,
99                platform: CommonPlatform::LinuxMusl,
100            }
101            .as_triple()
102        };
103
104        job = job.dep_on(&cfg_common_params);
105        job = job.dep_on(
106            |_| flowey_lib_hvlite::_jobs::cfg_hvlite_reposource::Params {
107                hvlite_repo_source: openvmm_repo_source.clone(),
108            },
109        );
110        job = job.dep_on(|ctx| {
111            flowey_lib_hvlite::_jobs::build_openhcl_igvm_from_recipe_nix::Params {
112                arch: recipe_arch,
113                kernel_kind,
114                igvm_files: vec![igvm_file]
115                    .into_iter()
116                    .map(|recipe| OpenhclIgvmBuildParams {
117                        profile: openvmm_hcl_profile,
118                        recipe,
119                        custom_target: Some(CommonTriple::Custom(openhcl_musl_target(recipe_arch))),
120                    })
121                    .collect(),
122                artifact_dir_openhcl_igvm: ctx.publish_artifact(pub_openhcl_igvm),
123                artifact_dir_openhcl_igvm_extras: ctx.publish_artifact(pub_openhcl_igvm_extras),
124                artifact_openhcl_verify_size_baseline: None,
125                done: ctx.new_done_handle(),
126            }
127        });
128
129        job.finish();
130
131        Ok(pipeline)
132    }
133}