Skip to main content

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