flowey_hvlite/pipelines/
checkin_gates.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! See [`CheckinGatesCli`]
5
6use flowey::node::prelude::AdoResourcesRepositoryId;
7use flowey::node::prelude::FlowPlatformLinuxDistro;
8use flowey::node::prelude::GhPermission;
9use flowey::node::prelude::GhPermissionValue;
10use flowey::node::prelude::ReadVar;
11use flowey::pipeline::prelude::*;
12use flowey_lib_common::git_checkout::RepoSource;
13use flowey_lib_hvlite::_jobs::build_and_publish_openhcl_igvm_from_recipe::OpenhclIgvmBuildParams;
14use flowey_lib_hvlite::build_openhcl_igvm_from_recipe::OpenhclIgvmRecipe;
15use flowey_lib_hvlite::build_openvmm_hcl::OpenvmmHclBuildProfile;
16use flowey_lib_hvlite::run_cargo_build::common::CommonArch;
17use flowey_lib_hvlite::run_cargo_build::common::CommonPlatform;
18use flowey_lib_hvlite::run_cargo_build::common::CommonProfile;
19use flowey_lib_hvlite::run_cargo_build::common::CommonTriple;
20use std::collections::BTreeMap;
21use std::path::PathBuf;
22use target_lexicon::Triple;
23use vmm_test_images::KnownTestArtifacts;
24
25#[derive(Copy, Clone, clap::ValueEnum)]
26enum PipelineConfig {
27    /// Run on all PRs targeting the OpenVMM GitHub repo.
28    Pr,
29    /// Run on all commits that land in a branch.
30    ///
31    /// The key difference between the CI and PR pipelines is whether things are
32    /// being built in `release` mode.
33    Ci,
34    /// Release variant of the `Pr` pipeline.
35    PrRelease,
36}
37
38/// A unified pipeline defining all checkin gates required to land a commit in
39/// the OpenVMM repo.
40#[derive(clap::Args)]
41pub struct CheckinGatesCli {
42    /// Which pipeline configuration to use.
43    #[clap(long)]
44    config: PipelineConfig,
45
46    #[clap(flatten)]
47    local_run_args: Option<crate::pipelines_shared::cfg_common_params::LocalRunArgs>,
48
49    /// Set custom path to search for / download VMM tests disk-images
50    #[clap(long)]
51    vmm_tests_disk_cache_dir: Option<PathBuf>,
52}
53
54impl IntoPipeline for CheckinGatesCli {
55    fn into_pipeline(self, backend_hint: PipelineBackendHint) -> anyhow::Result<Pipeline> {
56        let Self {
57            config,
58            local_run_args,
59            vmm_tests_disk_cache_dir,
60        } = self;
61
62        let release = match config {
63            PipelineConfig::Ci | PipelineConfig::PrRelease => true,
64            PipelineConfig::Pr => false,
65        };
66
67        let mut pipeline = Pipeline::new();
68
69        let mut vmgstools = BTreeMap::new();
70
71        // configure pr/ci branch triggers and add gh pipeline name
72        {
73            let branches = vec!["main".into(), "release/*".into()];
74            match config {
75                PipelineConfig::Ci => {
76                    pipeline
77                        .gh_set_ci_triggers(GhCiTriggers {
78                            branches,
79                            ..Default::default()
80                        })
81                        .gh_set_name("OpenVMM CI");
82                }
83                PipelineConfig::Pr => {
84                    pipeline
85                        .gh_set_pr_triggers(GhPrTriggers {
86                            branches,
87                            ..GhPrTriggers::new_draftable()
88                        })
89                        .gh_set_name("OpenVMM PR")
90                        .ado_set_pr_triggers(AdoPrTriggers {
91                            branches: vec!["main".into(), "release/*".into(), "embargo/*".into()],
92                            ..Default::default()
93                        });
94                }
95                PipelineConfig::PrRelease => {
96                    // This workflow is triggered when a specific label is present on a PR.
97                    let mut triggers = GhPrTriggers::new_draftable();
98                    triggers.branches = branches;
99                    triggers.types.push("labeled".into());
100                    pipeline
101                        .gh_set_pr_triggers(triggers)
102                        .gh_set_name("[Optional] OpenVMM Release PR");
103                }
104            }
105        }
106
107        let openvmm_repo_source = match backend_hint {
108            PipelineBackendHint::Local => {
109                RepoSource::ExistingClone(ReadVar::from_static(crate::repo_root()))
110            }
111            PipelineBackendHint::Github => RepoSource::GithubSelf,
112            PipelineBackendHint::Ado => {
113                RepoSource::AdoResource(AdoResourcesRepositoryId::new_self())
114            }
115        };
116
117        if let RepoSource::GithubSelf = &openvmm_repo_source {
118            pipeline.gh_set_flowey_bootstrap_template(
119                crate::pipelines_shared::gh_flowey_bootstrap_template::get_template(),
120            );
121        }
122
123        if let RepoSource::AdoResource(source) = &openvmm_repo_source {
124            pipeline.ado_set_flowey_bootstrap_template(
125                crate::pipelines_shared::ado_flowey_bootstrap_template::get_template_ado(source),
126            );
127        }
128
129        let cfg_common_params = crate::pipelines_shared::cfg_common_params::get_cfg_common_params(
130            &mut pipeline,
131            backend_hint,
132            local_run_args,
133        )?;
134
135        pipeline.inject_all_jobs_with(move |job| {
136            let mut job = job
137                .dep_on(&cfg_common_params)
138                .dep_on(|_| flowey_lib_hvlite::_jobs::cfg_versions::Request::Init)
139                .dep_on(
140                    |_| flowey_lib_hvlite::_jobs::cfg_hvlite_reposource::Params {
141                        hvlite_repo_source: openvmm_repo_source.clone(),
142                    },
143                )
144                .gh_grant_permissions::<flowey_lib_common::git_checkout::Node>([(
145                    GhPermission::Contents,
146                    GhPermissionValue::Read,
147                )])
148                .gh_grant_permissions::<flowey_lib_common::gh_task_azure_login::Node>([(
149                    GhPermission::IdToken,
150                    GhPermissionValue::Write,
151                )]);
152
153            // For the release pipeline, only run if the "release-ci-required" label is present and PR is not draft
154            if matches!(config, PipelineConfig::PrRelease) {
155                job = job.gh_dangerous_override_if(
156                    "contains(github.event.pull_request.labels.*.name, 'release-ci-required') && github.event.pull_request.draft == false",
157                );
158            }
159
160            job
161        });
162
163        let openhcl_musl_target = |arch: CommonArch| -> Triple {
164            CommonTriple::Common {
165                arch,
166                platform: CommonPlatform::LinuxMusl,
167            }
168            .as_triple()
169        };
170
171        // initialize the various VMM tests nextest archive artifacts
172        let (pub_vmm_tests_archive_linux_x86, use_vmm_tests_archive_linux_x86) =
173            pipeline.new_typed_artifact("x64-linux-vmm-tests-archive");
174        let (pub_vmm_tests_archive_windows_x86, use_vmm_tests_archive_windows_x86) =
175            pipeline.new_typed_artifact("x64-windows-vmm-tests-archive");
176        let (pub_vmm_tests_archive_windows_aarch64, use_vmm_tests_archive_windows_aarch64) =
177            pipeline.new_typed_artifact("aarch64-windows-vmm-tests-archive");
178
179        // wrap each publish handle in an option, so downstream code can
180        // `.take()` the handle when emitting the corresponding job
181        let mut pub_vmm_tests_archive_linux_x86 = Some(pub_vmm_tests_archive_linux_x86);
182        let mut pub_vmm_tests_archive_windows_x86 = Some(pub_vmm_tests_archive_windows_x86);
183        let mut pub_vmm_tests_archive_windows_aarch64 = Some(pub_vmm_tests_archive_windows_aarch64);
184
185        // initialize the various "VmmTestsArtifactsBuilder" containers, which
186        // are used to "skim off" various artifacts that the VMM test jobs
187        // require.
188        let mut vmm_tests_artifacts_linux_x86 =
189            vmm_tests_artifact_builders::VmmTestsArtifactsBuilderLinuxX86::default();
190        let mut vmm_tests_artifacts_windows_x86 =
191            vmm_tests_artifact_builders::VmmTestsArtifactsBuilderWindowsX86::default();
192        let mut vmm_tests_artifacts_windows_aarch64 =
193            vmm_tests_artifact_builders::VmmTestsArtifactsBuilderWindowsAarch64::default();
194
195        // We need to maintain a list of all jobs, so we can hang the "all good"
196        // job off of them. This is requires because github status checks only allow
197        // specifying jobs, and not workflows.
198        // There's more info in the following discussion:
199        // <https://github.com/orgs/community/discussions/12395>
200        let mut all_jobs = Vec::new();
201
202        // emit xtask fmt job
203        {
204            let windows_fmt_job = pipeline
205                .new_job(
206                    FlowPlatform::Windows,
207                    FlowArch::X86_64,
208                    "xtask fmt (windows)",
209                )
210                .gh_set_pool(crate::pipelines_shared::gh_pools::gh_hosted_x64_windows())
211                .ado_set_pool(crate::pipelines_shared::ado_pools::default_x86_pool(
212                    FlowPlatform::Windows,
213                ))
214                .dep_on(|ctx| flowey_lib_hvlite::_jobs::check_xtask_fmt::Request {
215                    target: CommonTriple::X86_64_WINDOWS_MSVC,
216                    done: ctx.new_done_handle(),
217                })
218                .finish();
219
220            let linux_fmt_job = pipeline
221                .new_job(
222                    FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
223                    FlowArch::X86_64,
224                    "xtask fmt (linux)",
225                )
226                .gh_set_pool(crate::pipelines_shared::gh_pools::gh_hosted_x64_linux())
227                .ado_set_pool(crate::pipelines_shared::ado_pools::default_x86_pool(
228                    FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
229                ))
230                .dep_on(|ctx| flowey_lib_hvlite::_jobs::check_xtask_fmt::Request {
231                    target: CommonTriple::X86_64_LINUX_GNU,
232                    done: ctx.new_done_handle(),
233                })
234                .finish();
235
236            // cut down on extra noise by having the linux check run first, and
237            // then if it passes, run the windows checks just in case there is a
238            // difference between the two.
239            pipeline.non_artifact_dep(&windows_fmt_job, &linux_fmt_job);
240
241            all_jobs.push(linux_fmt_job);
242            all_jobs.push(windows_fmt_job);
243        }
244
245        // emit windows build machine jobs
246        //
247        // In order to ensure we start running VMM tests as soon as possible, we emit
248        // two separate windows job per arch - one for artifacts in the VMM tests
249        // hotpath, and another for any auxiliary artifacts that aren't
250        // required by VMM tests.
251        for arch in [CommonArch::Aarch64, CommonArch::X86_64] {
252            let arch_tag = match arch {
253                CommonArch::X86_64 => "x64",
254                CommonArch::Aarch64 => "aarch64",
255            };
256
257            // artifacts which _are_ in the VMM tests "hot path"
258            let (pub_openvmm, use_openvmm) =
259                pipeline.new_typed_artifact(format!("{arch_tag}-windows-openvmm"));
260
261            let (pub_pipette_windows, use_pipette_windows) =
262                pipeline.new_typed_artifact(format!("{arch_tag}-windows-pipette"));
263
264            let (pub_tmk_vmm, use_tmk_vmm) =
265                pipeline.new_typed_artifact(format!("{arch_tag}-windows-tmk_vmm"));
266
267            let (pub_prep_steps, use_prep_steps) =
268                pipeline.new_typed_artifact(format!("{arch_tag}-windows-prep_steps"));
269
270            let (pub_vmgstool, use_vmgstool) =
271                pipeline.new_typed_artifact(format!("{arch_tag}-windows-vmgstool"));
272
273            let (pub_tpm_guest_tests, use_tpm_guest_tests_windows) =
274                pipeline.new_typed_artifact(format!("{arch_tag}-windows-tpm_guest_tests"));
275
276            let (pub_test_igvm_agent_rpc_server, use_test_igvm_agent_rpc_server) = pipeline
277                .new_typed_artifact(format!("{arch_tag}-windows-test_igvm_agent_rpc_server"));
278
279            // filter off interesting artifacts required by the VMM tests job
280            match arch {
281                CommonArch::X86_64 => {
282                    vmm_tests_artifacts_linux_x86.use_pipette_windows =
283                        Some(use_pipette_windows.clone());
284                    vmm_tests_artifacts_windows_x86.use_openvmm = Some(use_openvmm.clone());
285                    vmm_tests_artifacts_windows_x86.use_pipette_windows =
286                        Some(use_pipette_windows.clone());
287                    vmm_tests_artifacts_windows_x86.use_tmk_vmm = Some(use_tmk_vmm.clone());
288                    vmm_tests_artifacts_windows_x86.use_prep_steps = Some(use_prep_steps.clone());
289                    vmm_tests_artifacts_windows_x86.use_vmgstool = Some(use_vmgstool.clone());
290                    vmm_tests_artifacts_windows_x86.use_tpm_guest_tests_windows =
291                        Some(use_tpm_guest_tests_windows.clone());
292                    vmm_tests_artifacts_windows_x86.use_test_igvm_agent_rpc_server =
293                        Some(use_test_igvm_agent_rpc_server.clone());
294                }
295                CommonArch::Aarch64 => {
296                    vmm_tests_artifacts_windows_aarch64.use_openvmm = Some(use_openvmm.clone());
297                    vmm_tests_artifacts_windows_aarch64.use_pipette_windows =
298                        Some(use_pipette_windows.clone());
299                    vmm_tests_artifacts_windows_aarch64.use_tmk_vmm = Some(use_tmk_vmm.clone());
300                    vmm_tests_artifacts_windows_aarch64.use_vmgstool = Some(use_vmgstool.clone());
301                }
302            }
303            // emit a job for artifacts which _are not_ in the VMM tests "hot
304            // path"
305            // artifacts which _are not_ in the VMM tests "hot path"
306            let (pub_igvmfilegen, _use_igvmfilegen) =
307                pipeline.new_typed_artifact(format!("{arch_tag}-windows-igvmfilegen"));
308            let (pub_vmgs_lib, _use_vmgs_lib) =
309                pipeline.new_typed_artifact(format!("{arch_tag}-windows-vmgs_lib"));
310            let (pub_hypestv, _use_hypestv) =
311                pipeline.new_typed_artifact(format!("{arch_tag}-windows-hypestv"));
312            let (pub_ohcldiag_dev, _use_ohcldiag_dev) =
313                pipeline.new_typed_artifact(format!("{arch_tag}-windows-ohcldiag-dev"));
314
315            let job = pipeline
316                .new_job(
317                    FlowPlatform::Windows,
318                    FlowArch::X86_64,
319                    format!("build artifacts (not for VMM tests) [{arch_tag}-windows]"),
320                )
321                .gh_set_pool(crate::pipelines_shared::gh_pools::windows_amd_self_hosted_largedisk())
322                .ado_set_pool(crate::pipelines_shared::ado_pools::default_x86_pool(
323                    FlowPlatform::Windows,
324                ))
325                .dep_on(|ctx| flowey_lib_hvlite::build_hypestv::Request {
326                    target: CommonTriple::Common {
327                        arch,
328                        platform: CommonPlatform::WindowsMsvc,
329                    },
330                    profile: CommonProfile::from_release(release),
331                    hypestv: ctx.publish_typed_artifact(pub_hypestv),
332                })
333                .dep_on(|ctx| flowey_lib_hvlite::build_and_test_vmgs_lib::Request {
334                    target: CommonTriple::Common {
335                        arch,
336                        platform: CommonPlatform::WindowsMsvc,
337                    },
338                    profile: CommonProfile::from_release(release),
339                    vmgs_lib: ctx.publish_typed_artifact(pub_vmgs_lib),
340                })
341                .dep_on(|ctx| flowey_lib_hvlite::build_igvmfilegen::Request {
342                    build_params: flowey_lib_hvlite::build_igvmfilegen::IgvmfilegenBuildParams {
343                        target: CommonTriple::Common {
344                            arch,
345                            platform: CommonPlatform::WindowsMsvc,
346                        },
347                        profile: CommonProfile::from_release(release).into(),
348                    },
349                    igvmfilegen: ctx.publish_typed_artifact(pub_igvmfilegen),
350                })
351                .dep_on(|ctx| flowey_lib_hvlite::build_ohcldiag_dev::Request {
352                    target: CommonTriple::Common {
353                        arch,
354                        platform: CommonPlatform::WindowsMsvc,
355                    },
356                    profile: CommonProfile::from_release(release),
357                    ohcldiag_dev: ctx.publish_typed_artifact(pub_ohcldiag_dev),
358                });
359
360            all_jobs.push(job.finish());
361
362            let vmgstool_target = CommonTriple::Common {
363                arch,
364                platform: CommonPlatform::WindowsMsvc,
365            };
366            if vmgstools
367                .insert(vmgstool_target.to_string(), use_vmgstool.clone())
368                .is_some()
369            {
370                anyhow::bail!("multiple vmgstools for the same target");
371            }
372
373            // emit a job for artifacts which _are_ in the VMM tests "hot path"
374            let mut job = pipeline
375                .new_job(
376                    FlowPlatform::Windows,
377                    FlowArch::X86_64,
378                    format!("build artifacts (for VMM tests) [{arch_tag}-windows]"),
379                )
380                .gh_set_pool(crate::pipelines_shared::gh_pools::windows_amd_self_hosted_largedisk())
381                .ado_set_pool(crate::pipelines_shared::ado_pools::default_x86_pool(
382                    FlowPlatform::Windows,
383                ))
384                .dep_on(|ctx| {
385                    flowey_lib_hvlite::build_openvmm::Request {
386                        params: flowey_lib_hvlite::build_openvmm::OpenvmmBuildParams {
387                            target: CommonTriple::Common {
388                                arch,
389                                platform: CommonPlatform::WindowsMsvc,
390                            },
391                            profile: CommonProfile::from_release(release),
392                            // FIXME: this relies on openvmm default features
393                            // Our ARM test runners need the latest WHP changes
394                            features: if matches!(arch, CommonArch::Aarch64) {
395                                [flowey_lib_hvlite::build_openvmm::OpenvmmFeature::UnstableWhp]
396                                    .into()
397                            } else {
398                                [].into()
399                            },
400                        },
401                        openvmm: ctx.publish_typed_artifact(pub_openvmm),
402                    }
403                })
404                .dep_on(|ctx| flowey_lib_hvlite::build_pipette::Request {
405                    target: CommonTriple::Common {
406                        arch,
407                        platform: CommonPlatform::WindowsMsvc,
408                    },
409                    profile: CommonProfile::from_release(release),
410                    pipette: ctx.publish_typed_artifact(pub_pipette_windows),
411                })
412                .dep_on(|ctx| flowey_lib_hvlite::build_tmk_vmm::Request {
413                    target: CommonTriple::Common {
414                        arch,
415                        platform: CommonPlatform::WindowsMsvc,
416                    },
417                    unstable_whp: true, // The ARM64 CI runner supports the unstable WHP interface
418                    profile: CommonProfile::from_release(release),
419                    tmk_vmm: ctx.publish_typed_artifact(pub_tmk_vmm),
420                })
421                .dep_on(|ctx| flowey_lib_hvlite::build_prep_steps::Request {
422                    target: CommonTriple::Common {
423                        arch,
424                        platform: CommonPlatform::WindowsMsvc,
425                    },
426                    profile: CommonProfile::from_release(release),
427                    prep_steps: ctx.publish_typed_artifact(pub_prep_steps),
428                })
429                .dep_on(|ctx| flowey_lib_hvlite::build_vmgstool::Request {
430                    target: vmgstool_target,
431                    profile: CommonProfile::from_release(release),
432                    with_crypto: true,
433                    with_test_helpers: true,
434                    vmgstool: ctx.publish_typed_artifact(pub_vmgstool),
435                })
436                .dep_on(|ctx| flowey_lib_hvlite::build_tpm_guest_tests::Request {
437                    target: CommonTriple::Common {
438                        arch,
439                        platform: CommonPlatform::WindowsMsvc,
440                    },
441                    profile: CommonProfile::from_release(release),
442                    tpm_guest_tests: ctx.publish_typed_artifact(pub_tpm_guest_tests),
443                })
444                .dep_on(
445                    |ctx| flowey_lib_hvlite::build_test_igvm_agent_rpc_server::Request {
446                        target: CommonTriple::Common {
447                            arch,
448                            platform: CommonPlatform::WindowsMsvc,
449                        },
450                        profile: CommonProfile::from_release(release),
451                        test_igvm_agent_rpc_server: ctx
452                            .publish_typed_artifact(pub_test_igvm_agent_rpc_server),
453                    },
454                );
455
456            // Hang building the windows VMM tests off this big windows job.
457            match arch {
458                CommonArch::X86_64 => {
459                    let pub_vmm_tests_archive_windows_x86 =
460                        pub_vmm_tests_archive_windows_x86.take().unwrap();
461                    job = job.dep_on(|ctx|
462                        flowey_lib_hvlite::build_nextest_vmm_tests::Request {
463                        target: CommonTriple::X86_64_WINDOWS_MSVC.as_triple(),
464                        profile: CommonProfile::from_release(release),
465                        build_mode: flowey_lib_hvlite::build_nextest_vmm_tests::BuildNextestVmmTestsMode::Archive(
466                            ctx.publish_typed_artifact(pub_vmm_tests_archive_windows_x86),
467                        ),
468                    });
469                }
470                CommonArch::Aarch64 => {
471                    let pub_vmm_tests_archive_windows_aarch64 =
472                        pub_vmm_tests_archive_windows_aarch64.take().unwrap();
473                    job = job.dep_on(|ctx| flowey_lib_hvlite::build_nextest_vmm_tests::Request {
474                        target: CommonTriple::AARCH64_WINDOWS_MSVC.as_triple(),
475                        profile: CommonProfile::from_release(release),
476                        build_mode: flowey_lib_hvlite::build_nextest_vmm_tests::BuildNextestVmmTestsMode::Archive(
477                            ctx.publish_typed_artifact(pub_vmm_tests_archive_windows_aarch64),
478                        ),
479                    });
480                }
481            }
482
483            all_jobs.push(job.finish());
484        }
485
486        // emit linux build machine jobs (without openhcl)
487        for arch in [CommonArch::Aarch64, CommonArch::X86_64] {
488            let arch_tag = match arch {
489                CommonArch::X86_64 => "x64",
490                CommonArch::Aarch64 => "aarch64",
491            };
492
493            let (pub_openvmm, use_openvmm) =
494                pipeline.new_typed_artifact(format!("{arch_tag}-linux-openvmm"));
495            let (pub_igvmfilegen, _) =
496                pipeline.new_typed_artifact(format!("{arch_tag}-linux-igvmfilegen"));
497            let (pub_vmgs_lib, _) =
498                pipeline.new_typed_artifact(format!("{arch_tag}-linux-vmgs_lib"));
499            let (pub_vmgstool, use_vmgstool) =
500                pipeline.new_typed_artifact(format!("{arch_tag}-linux-vmgstool"));
501            let (pub_ohcldiag_dev, _) =
502                pipeline.new_typed_artifact(format!("{arch_tag}-linux-ohcldiag-dev"));
503            let (pub_tmks, use_tmks) = pipeline.new_typed_artifact(format!("{arch_tag}-tmks"));
504            let (pub_tpm_guest_tests, use_tpm_guest_tests) =
505                pipeline.new_typed_artifact(format!("{arch_tag}-linux-tpm_guest_tests"));
506
507            // NOTE: the choice to build it as part of this linux job was pretty
508            // arbitrary. It could just as well hang off the windows job.
509            //
510            // At this time though, having it here results in a net-reduction in
511            // E2E pipeline times, owing to how the VMM tests artifact dependency
512            // graph looks like.
513            let (pub_guest_test_uefi, use_guest_test_uefi) =
514                pipeline.new_typed_artifact(format!("{arch_tag}-guest_test_uefi"));
515
516            // skim off interesting artifacts required by the VMM tests job
517            match arch {
518                CommonArch::X86_64 => {
519                    vmm_tests_artifacts_linux_x86.use_openvmm = Some(use_openvmm.clone());
520                    vmm_tests_artifacts_linux_x86.use_guest_test_uefi =
521                        Some(use_guest_test_uefi.clone());
522                    vmm_tests_artifacts_windows_x86.use_guest_test_uefi =
523                        Some(use_guest_test_uefi.clone());
524                    vmm_tests_artifacts_windows_x86.use_tmks = Some(use_tmks.clone());
525                    vmm_tests_artifacts_linux_x86.use_tmks = Some(use_tmks.clone());
526                    vmm_tests_artifacts_windows_x86.use_tpm_guest_tests_linux =
527                        Some(use_tpm_guest_tests.clone());
528                }
529                CommonArch::Aarch64 => {
530                    vmm_tests_artifacts_windows_aarch64.use_guest_test_uefi =
531                        Some(use_guest_test_uefi.clone());
532                    vmm_tests_artifacts_windows_aarch64.use_tmks = Some(use_tmks.clone());
533                }
534            }
535
536            let vmgstool_target = CommonTriple::Common {
537                arch,
538                platform: CommonPlatform::LinuxGnu,
539            };
540            if vmgstools
541                .insert(vmgstool_target.to_string(), use_vmgstool.clone())
542                .is_some()
543            {
544                anyhow::bail!("multiple vmgstools for the same target");
545            }
546
547            let mut job = pipeline
548                .new_job(
549                    FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
550                    FlowArch::X86_64,
551                    format!("build artifacts [{arch_tag}-linux]"),
552                )
553                .gh_set_pool(crate::pipelines_shared::gh_pools::linux_self_hosted_largedisk())
554                .ado_set_pool(crate::pipelines_shared::ado_pools::default_x86_pool(
555                    FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
556                ))
557                .dep_on(|ctx| {
558                    flowey_lib_hvlite::build_openvmm::Request {
559                        params: flowey_lib_hvlite::build_openvmm::OpenvmmBuildParams {
560                            target: CommonTriple::Common {
561                                arch,
562                                platform: CommonPlatform::LinuxGnu,
563                            },
564                            profile: CommonProfile::from_release(release),
565                            // FIXME: this relies on openvmm default features
566                            features: [flowey_lib_hvlite::build_openvmm::OpenvmmFeature::Tpm]
567                                .into(),
568                        },
569                        openvmm: ctx.publish_typed_artifact(pub_openvmm),
570                    }
571                })
572                .dep_on(|ctx| flowey_lib_hvlite::build_vmgstool::Request {
573                    target: vmgstool_target,
574                    profile: CommonProfile::from_release(release),
575                    with_crypto: true,
576                    with_test_helpers: true,
577                    vmgstool: ctx.publish_typed_artifact(pub_vmgstool),
578                })
579                .dep_on(|ctx| flowey_lib_hvlite::build_and_test_vmgs_lib::Request {
580                    target: CommonTriple::Common {
581                        arch,
582                        platform: CommonPlatform::LinuxGnu,
583                    },
584                    profile: CommonProfile::from_release(release),
585                    vmgs_lib: ctx.publish_typed_artifact(pub_vmgs_lib),
586                })
587                .dep_on(|ctx| flowey_lib_hvlite::build_igvmfilegen::Request {
588                    build_params: flowey_lib_hvlite::build_igvmfilegen::IgvmfilegenBuildParams {
589                        target: CommonTriple::Common {
590                            arch,
591                            platform: CommonPlatform::LinuxGnu,
592                        },
593                        profile: CommonProfile::from_release(release).into(),
594                    },
595                    igvmfilegen: ctx.publish_typed_artifact(pub_igvmfilegen),
596                })
597                .dep_on(|ctx| flowey_lib_hvlite::build_ohcldiag_dev::Request {
598                    target: CommonTriple::Common {
599                        arch,
600                        platform: CommonPlatform::LinuxGnu,
601                    },
602                    profile: CommonProfile::from_release(release),
603                    ohcldiag_dev: ctx.publish_typed_artifact(pub_ohcldiag_dev),
604                })
605                .dep_on(|ctx| flowey_lib_hvlite::build_guest_test_uefi::Request {
606                    arch,
607                    profile: CommonProfile::from_release(release),
608                    guest_test_uefi: ctx.publish_typed_artifact(pub_guest_test_uefi),
609                })
610                .dep_on(|ctx| flowey_lib_hvlite::build_tmks::Request {
611                    arch,
612                    profile: CommonProfile::from_release(release),
613                    tmks: ctx.publish_typed_artifact(pub_tmks),
614                })
615                .dep_on(|ctx| flowey_lib_hvlite::build_tpm_guest_tests::Request {
616                    target: CommonTriple::Common {
617                        arch,
618                        platform: CommonPlatform::LinuxGnu,
619                    },
620                    profile: CommonProfile::from_release(release),
621                    tpm_guest_tests: ctx.publish_typed_artifact(pub_tpm_guest_tests),
622                });
623
624            // Hang building the linux VMM tests off this big linux job.
625            //
626            // No ARM64 VMM tests yet
627            if matches!(arch, CommonArch::X86_64) {
628                let pub_vmm_tests_archive_linux_x86 =
629                    pub_vmm_tests_archive_linux_x86.take().unwrap();
630                job = job.dep_on(|ctx| flowey_lib_hvlite::build_nextest_vmm_tests::Request {
631                    target: CommonTriple::X86_64_LINUX_GNU.as_triple(),
632                    profile: CommonProfile::from_release(release),
633                    build_mode: flowey_lib_hvlite::build_nextest_vmm_tests::BuildNextestVmmTestsMode::Archive(
634                        ctx.publish_typed_artifact(pub_vmm_tests_archive_linux_x86),
635                    ),
636                });
637            }
638
639            all_jobs.push(job.finish());
640        }
641
642        // emit openhcl build job
643        for arch in [CommonArch::Aarch64, CommonArch::X86_64] {
644            let arch_tag = match arch {
645                CommonArch::X86_64 => "x64",
646                CommonArch::Aarch64 => "aarch64",
647            };
648
649            let openvmm_hcl_profile = if release {
650                OpenvmmHclBuildProfile::OpenvmmHclShip
651            } else {
652                OpenvmmHclBuildProfile::Debug
653            };
654
655            let (pub_openhcl_igvm, use_openhcl_igvm) =
656                pipeline.new_artifact(format!("{arch_tag}-openhcl-igvm"));
657            let (pub_openhcl_igvm_extras, _use_openhcl_igvm_extras) =
658                pipeline.new_artifact(format!("{arch_tag}-openhcl-igvm-extras"));
659
660            let (pub_openhcl_baseline, _use_openhcl_baseline) =
661                if matches!(config, PipelineConfig::Ci) {
662                    let (p, u) = pipeline.new_artifact(format!("{arch_tag}-openhcl-baseline"));
663                    (Some(p), Some(u))
664                } else {
665                    (None, None)
666                };
667
668            // also build pipette musl on this job, as until we land the
669            // refactor that allows building musl without the full openhcl
670            // toolchain, it would require pulling in all the openhcl
671            // toolchain deps...
672            let (pub_pipette_linux_musl, use_pipette_linux_musl) =
673                pipeline.new_typed_artifact(format!("{arch_tag}-linux-musl-pipette"));
674
675            let (pub_tmk_vmm, use_tmk_vmm) =
676                pipeline.new_typed_artifact(format!("{arch_tag}-linux-musl-tmk_vmm"));
677
678            // skim off interesting artifacts required by the VMM tests job
679            match arch {
680                CommonArch::X86_64 => {
681                    vmm_tests_artifacts_windows_x86.use_openhcl_igvm_files =
682                        Some(use_openhcl_igvm.clone());
683                    vmm_tests_artifacts_windows_x86.use_pipette_linux_musl =
684                        Some(use_pipette_linux_musl.clone());
685                    vmm_tests_artifacts_linux_x86.use_pipette_linux_musl =
686                        Some(use_pipette_linux_musl.clone());
687                    vmm_tests_artifacts_linux_x86.use_tmk_vmm = Some(use_tmk_vmm.clone());
688                    vmm_tests_artifacts_windows_x86.use_tmk_vmm_linux_musl =
689                        Some(use_tmk_vmm.clone());
690                }
691                CommonArch::Aarch64 => {
692                    vmm_tests_artifacts_windows_aarch64.use_openhcl_igvm_files =
693                        Some(use_openhcl_igvm.clone());
694                    vmm_tests_artifacts_windows_aarch64.use_pipette_linux_musl =
695                        Some(use_pipette_linux_musl.clone());
696                    vmm_tests_artifacts_windows_aarch64.use_tmk_vmm_linux_musl =
697                        Some(use_tmk_vmm.clone());
698                }
699            }
700            let igvm_recipes = match arch {
701                CommonArch::X86_64 => vec![
702                    OpenhclIgvmRecipe::X64,
703                    OpenhclIgvmRecipe::X64Devkern,
704                    OpenhclIgvmRecipe::X64TestLinuxDirect,
705                    OpenhclIgvmRecipe::X64TestLinuxDirectDevkern,
706                    OpenhclIgvmRecipe::X64Cvm,
707                ],
708                CommonArch::Aarch64 => {
709                    vec![
710                        OpenhclIgvmRecipe::Aarch64,
711                        OpenhclIgvmRecipe::Aarch64Devkern,
712                    ]
713                }
714            };
715
716            let build_openhcl_job_tag = |arch_tag| format!("build openhcl [{arch_tag}-linux]");
717            let job = pipeline
718                .new_job(
719                    FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
720                    FlowArch::X86_64,
721                    build_openhcl_job_tag(arch_tag),
722                )
723                .gh_set_pool(crate::pipelines_shared::gh_pools::linux_self_hosted_largedisk())
724                .ado_set_pool(crate::pipelines_shared::ado_pools::default_x86_pool(
725                    FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
726                ))
727                .dep_on(|ctx| {
728                    let publish_baseline_artifact = pub_openhcl_baseline
729                        .map(|baseline_artifact| ctx.publish_artifact(baseline_artifact));
730
731                    flowey_lib_hvlite::_jobs::build_and_publish_openhcl_igvm_from_recipe::Params {
732                        igvm_files: igvm_recipes
733                            .clone()
734                            .into_iter()
735                            .map(|recipe| OpenhclIgvmBuildParams {
736                                profile: openvmm_hcl_profile,
737                                recipe,
738                                custom_target: Some(CommonTriple::Custom(openhcl_musl_target(
739                                    arch,
740                                ))),
741                            })
742                            .collect(),
743                        artifact_dir_openhcl_igvm: ctx.publish_artifact(pub_openhcl_igvm),
744                        artifact_dir_openhcl_igvm_extras: ctx
745                            .publish_artifact(pub_openhcl_igvm_extras),
746                        artifact_openhcl_verify_size_baseline: publish_baseline_artifact,
747                        done: ctx.new_done_handle(),
748                    }
749                })
750                .dep_on(|ctx| flowey_lib_hvlite::build_pipette::Request {
751                    target: CommonTriple::Common {
752                        arch,
753                        platform: CommonPlatform::LinuxMusl,
754                    },
755                    profile: CommonProfile::from_release(release),
756                    pipette: ctx.publish_typed_artifact(pub_pipette_linux_musl),
757                })
758                .dep_on(|ctx| flowey_lib_hvlite::build_tmk_vmm::Request {
759                    target: CommonTriple::Common {
760                        arch,
761                        platform: CommonPlatform::LinuxMusl,
762                    },
763                    profile: CommonProfile::from_release(release),
764                    unstable_whp: false,
765                    tmk_vmm: ctx.publish_typed_artifact(pub_tmk_vmm),
766                });
767
768            all_jobs.push(job.finish());
769
770            // TODO: Once we have a few runs of the openvmm-mirror PR pipeline, this job can be re-worked to use ADO artifacts instead of GH artifacts.
771            if matches!(config, PipelineConfig::Pr)
772                && !matches!(backend_hint, PipelineBackendHint::Ado)
773            {
774                let job = pipeline
775                    .new_job(
776                        FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
777                        FlowArch::X86_64,
778                        format!("verify openhcl binary size [{}]", arch_tag),
779                    )
780                    .gh_set_pool(crate::pipelines_shared::gh_pools::gh_hosted_x64_linux())
781                    .dep_on(
782                        |ctx| flowey_lib_hvlite::_jobs::check_openvmm_hcl_size::Request {
783                            target: CommonTriple::Common {
784                                arch,
785                                platform: CommonPlatform::LinuxMusl,
786                            },
787                            done: ctx.new_done_handle(),
788                            pipeline_name: "openvmm-ci.yaml".into(),
789                            job_name: build_openhcl_job_tag(arch_tag),
790                        },
791                    )
792                    .finish();
793                all_jobs.push(job);
794            }
795        }
796
797        // Emit clippy + unit-test jobs
798        //
799        // The only reason we bundle clippy and unit-tests together is to avoid
800        // requiring another build agent.
801        struct ClippyUnitTestJobParams<'a> {
802            platform: FlowPlatform,
803            arch: FlowArch,
804            gh_pool: GhRunner,
805            clippy_targets: Option<(&'a str, &'a [(Triple, bool)])>,
806            unit_test_target: Option<(&'a str, Triple)>,
807        }
808
809        for ClippyUnitTestJobParams {
810            platform,
811            arch,
812            gh_pool,
813            clippy_targets,
814            unit_test_target,
815        } in [
816            ClippyUnitTestJobParams {
817                platform: FlowPlatform::Windows,
818                arch: FlowArch::X86_64,
819                gh_pool: if release {
820                    crate::pipelines_shared::gh_pools::windows_amd_self_hosted_largedisk()
821                } else {
822                    crate::pipelines_shared::gh_pools::gh_hosted_x64_windows()
823                },
824                clippy_targets: Some((
825                    "x64-windows",
826                    &[(target_lexicon::triple!("x86_64-pc-windows-msvc"), false)],
827                )),
828                unit_test_target: Some((
829                    "x64-windows",
830                    target_lexicon::triple!("x86_64-pc-windows-msvc"),
831                )),
832            },
833            ClippyUnitTestJobParams {
834                platform: FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
835                arch: FlowArch::X86_64,
836                // This job fails on github runners for an unknown reason, so
837                // use self-hosted runners for now.
838                gh_pool: crate::pipelines_shared::gh_pools::linux_self_hosted_largedisk(),
839                clippy_targets: Some((
840                    "x64-linux, macos",
841                    &[
842                        (target_lexicon::triple!("x86_64-unknown-linux-gnu"), false),
843                        (target_lexicon::triple!("aarch64-apple-darwin"), false),
844                    ],
845                )),
846                unit_test_target: Some((
847                    "x64-linux",
848                    target_lexicon::triple!("x86_64-unknown-linux-gnu"),
849                )),
850            },
851            ClippyUnitTestJobParams {
852                platform: FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
853                arch: FlowArch::X86_64,
854                // This job fails on github runners due to disk space exhaustion, so
855                // use self-hosted runners for now.
856                gh_pool: crate::pipelines_shared::gh_pools::linux_self_hosted_largedisk(),
857                clippy_targets: Some((
858                    "x64-linux-musl, misc nostd",
859                    &[(openhcl_musl_target(CommonArch::X86_64), true)],
860                )),
861                unit_test_target: Some(("x64-linux-musl", openhcl_musl_target(CommonArch::X86_64))),
862            },
863            ClippyUnitTestJobParams {
864                platform: FlowPlatform::Windows,
865                arch: FlowArch::Aarch64,
866                gh_pool: if release {
867                    crate::pipelines_shared::gh_pools::windows_arm_self_hosted()
868                } else {
869                    crate::pipelines_shared::gh_pools::gh_hosted_arm_windows()
870                },
871                clippy_targets: Some((
872                    "aarch64-windows",
873                    &[(target_lexicon::triple!("aarch64-pc-windows-msvc"), false)],
874                )),
875                unit_test_target: Some((
876                    "aarch64-windows",
877                    target_lexicon::triple!("aarch64-pc-windows-msvc"),
878                )),
879            },
880            ClippyUnitTestJobParams {
881                platform: FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
882                arch: FlowArch::Aarch64,
883                gh_pool: if release {
884                    crate::pipelines_shared::gh_pools::linux_arm_self_hosted()
885                } else {
886                    crate::pipelines_shared::gh_pools::gh_hosted_arm_linux()
887                },
888                clippy_targets: Some((
889                    "aarch64-linux",
890                    &[(target_lexicon::triple!("aarch64-unknown-linux-gnu"), false)],
891                )),
892                unit_test_target: Some((
893                    "aarch64-linux",
894                    target_lexicon::triple!("aarch64-unknown-linux-gnu"),
895                )),
896            },
897            ClippyUnitTestJobParams {
898                platform: FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
899                arch: FlowArch::Aarch64,
900                gh_pool: if release {
901                    crate::pipelines_shared::gh_pools::linux_arm_self_hosted()
902                } else {
903                    crate::pipelines_shared::gh_pools::gh_hosted_arm_linux()
904                },
905                clippy_targets: Some((
906                    "aarch64-linux-musl, misc nostd",
907                    &[(openhcl_musl_target(CommonArch::Aarch64), true)],
908                )),
909                unit_test_target: Some((
910                    "aarch64-linux-musl",
911                    openhcl_musl_target(CommonArch::Aarch64),
912                )),
913            },
914        ] {
915            // Skip ARM64 jobs entirely for ADO backend (there is no native ARM64 pool ADO)
916            if matches!(arch, FlowArch::Aarch64) && matches!(backend_hint, PipelineBackendHint::Ado)
917            {
918                continue;
919            }
920
921            let mut job_name = Vec::new();
922            if let Some((label, _)) = &clippy_targets {
923                job_name.push(format!("clippy [{label}]"));
924            }
925            if let Some((label, _)) = &unit_test_target {
926                job_name.push(format!("unit tests [{label}]"));
927            }
928            let job_name = job_name.join(", ");
929
930            let unit_test_target = unit_test_target.map(|(label, target)| {
931                let test_label = format!("{label}-unit-tests");
932                let pub_unit_test_junit_xml = if matches!(backend_hint, PipelineBackendHint::Local)
933                {
934                    Some(pipeline.new_artifact(&test_label).0)
935                } else {
936                    None
937                };
938                (test_label, target, pub_unit_test_junit_xml)
939            });
940
941            let mut clippy_unit_test_job = pipeline
942                .new_job(platform, arch, job_name)
943                .gh_set_pool(gh_pool)
944                .ado_set_pool(match platform {
945                    FlowPlatform::Windows => {
946                        crate::pipelines_shared::ado_pools::default_x86_pool(FlowPlatform::Windows)
947                    }
948                    FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu) => {
949                        crate::pipelines_shared::ado_pools::default_x86_pool(FlowPlatform::Linux(
950                            FlowPlatformLinuxDistro::Ubuntu,
951                        ))
952                    }
953                    _ => anyhow::bail!("unsupported platform"),
954                });
955
956            if let Some((_, targets)) = clippy_targets {
957                for (target, also_check_misc_nostd_crates) in targets {
958                    clippy_unit_test_job = clippy_unit_test_job.dep_on(|ctx| {
959                        flowey_lib_hvlite::_jobs::check_clippy::Request {
960                            target: target.clone(),
961                            profile: CommonProfile::from_release(release),
962                            done: ctx.new_done_handle(),
963                            also_check_misc_nostd_crates: *also_check_misc_nostd_crates,
964                        }
965                    });
966                }
967            }
968
969            if let Some((test_label, target, pub_unit_test_junit_xml)) = unit_test_target {
970                clippy_unit_test_job = clippy_unit_test_job
971                    .dep_on(|ctx| {
972                        flowey_lib_hvlite::_jobs::build_and_run_nextest_unit_tests::Params {
973                            junit_test_label: test_label,
974                            nextest_profile:
975                                flowey_lib_hvlite::run_cargo_nextest_run::NextestProfile::Ci,
976                            fail_job_on_test_fail: true,
977                            target: target.clone(),
978                            profile: CommonProfile::from_release(release),
979                            artifact_dir: pub_unit_test_junit_xml.map(|x| ctx.publish_artifact(x)),
980                            done: ctx.new_done_handle(),
981                        }
982                    })
983                    .dep_on(
984                        |ctx| flowey_lib_hvlite::_jobs::build_and_run_doc_tests::Params {
985                            target,
986                            profile: CommonProfile::from_release(release),
987                            done: ctx.new_done_handle(),
988                        },
989                    );
990            }
991
992            all_jobs.push(clippy_unit_test_job.finish());
993        }
994
995        let vmm_tests_artifacts_windows_intel_x86 = vmm_tests_artifacts_windows_x86
996            .clone()
997            .finish()
998            .map_err(|missing| {
999                anyhow::anyhow!("missing required windows-intel vmm_tests artifact: {missing}")
1000            })?;
1001        let vmm_tests_artifacts_windows_intel_tdx_x86 = vmm_tests_artifacts_windows_x86
1002            .clone()
1003            .finish()
1004            .map_err(|missing| {
1005                anyhow::anyhow!("missing required windows-intel-tdx vmm_tests artifact: {missing}")
1006            })?;
1007        let vmm_tests_artifacts_windows_amd_x86 = vmm_tests_artifacts_windows_x86
1008            .clone()
1009            .finish()
1010            .map_err(|missing| {
1011                anyhow::anyhow!("missing required windows-amd vmm_tests artifact: {missing}")
1012            })?;
1013        let vmm_tests_artifacts_windows_amd_snp_x86 = vmm_tests_artifacts_windows_x86
1014            .finish()
1015            .map_err(|missing| {
1016                anyhow::anyhow!("missing required windows-amd-snp vmm_tests artifact: {missing}")
1017            })?;
1018        let vmm_tests_artifacts_linux_x86 =
1019            vmm_tests_artifacts_linux_x86.finish().map_err(|missing| {
1020                anyhow::anyhow!("missing required linux vmm_tests artifact: {missing}")
1021            })?;
1022        let vmm_tests_artifacts_windows_aarch64 = vmm_tests_artifacts_windows_aarch64
1023            .finish()
1024            .map_err(|missing| {
1025                anyhow::anyhow!("missing required windows-aarch64 vmm_tests artifact: {missing}")
1026            })?;
1027
1028        // Emit VMM tests runner jobs
1029        struct VmmTestJobParams<'a> {
1030            platform: FlowPlatform,
1031            arch: FlowArch,
1032            gh_pool: GhRunner,
1033            label: &'a str,
1034            target: CommonTriple,
1035            resolve_vmm_tests_artifacts: vmm_tests_artifact_builders::ResolveVmmTestsDepArtifacts,
1036            nextest_filter_expr: String,
1037            test_artifacts: Vec<KnownTestArtifacts>,
1038            needs_prep_run: bool,
1039        }
1040
1041        let standard_filter = {
1042            // Standard VM-based CI machines should be able to run all tests except
1043            // those that require special hardware features (tdx/snp) or need to be
1044            // run on a baremetal host (hyper-v vbs doesn't seem to work nested).
1045            //
1046            // Run "very_heavy" tests that require lots of VPs on the self-hosted
1047            // CVM runners that have more cores.
1048            //
1049            // Even though OpenVMM + VBS + Windows tests can run on standard CI
1050            // machines, we exclude them here to avoid needing to run prep_steps
1051            // on non-self-hosted runners. This saves several minutes of CI time
1052            // that would be used for very few tests. We need to run prep_steps
1053            // on CVM runners anyways, so we might as well run those tests there.
1054            //
1055            // Our standard runners need to be updated to support Hyper-V OpenHCL
1056            // PCAT, so run those tests on the CVM runners for now.
1057            let mut filter = "all() & !test(very_heavy) & !test(openvmm_openhcl_uefi_x64_windows_datacenter_core_2025_x64_prepped_vbs) & !test(hyperv_openhcl_pcat)".to_string();
1058            // Currently, we don't have a good way for ADO runners to authenticate in GitHub
1059            // (that don't involve PATs) which is a requirement to download GH Workflow Artifacts
1060            // required by the upgrade and downgrade servicing tests. For now,
1061            // we will exclude these tests from running in the internal mirror.
1062            // Our standard runners also need to be updated to run Hyper-V
1063            // servicing tests.
1064            match backend_hint {
1065                PipelineBackendHint::Ado => {
1066                    filter.push_str(
1067                        " & !(test(servicing) & (test(upgrade) + test(downgrade) + test(hyperv)))",
1068                    );
1069                }
1070                _ => {
1071                    filter.push_str(" & !(test(servicing) & test(hyperv))");
1072                }
1073            }
1074            filter
1075        };
1076
1077        let standard_x64_test_artifacts = vec![
1078            KnownTestArtifacts::Alpine323X64Vhd,
1079            KnownTestArtifacts::FreeBsd13_2X64Vhd,
1080            KnownTestArtifacts::FreeBsd13_2X64Iso,
1081            KnownTestArtifacts::Gen1WindowsDataCenterCore2022X64Vhd,
1082            KnownTestArtifacts::Gen2WindowsDataCenterCore2022X64Vhd,
1083            KnownTestArtifacts::Gen2WindowsDataCenterCore2025X64Vhd,
1084            KnownTestArtifacts::Ubuntu2404ServerX64Vhd,
1085            KnownTestArtifacts::Ubuntu2504ServerX64Vhd,
1086            KnownTestArtifacts::VmgsWithBootEntry,
1087            KnownTestArtifacts::VmgsWith16kTpm,
1088        ];
1089
1090        let cvm_filter = |isolation_type| {
1091            let mut filter = format!(
1092                "test({isolation_type}) + (test(vbs) & test(hyperv)) + test(very_heavy) + test(openvmm_openhcl_uefi_x64_windows_datacenter_core_2025_x64_prepped_vbs)"
1093            );
1094            // OpenHCL PCAT tests are flakey on AMD SNP runners, so only run on TDX for now
1095            if isolation_type == "tdx" {
1096                filter.push_str(" + test(hyperv_openhcl_pcat)");
1097            }
1098
1099            // See comment for standard filter. Run hyper-v servicing tests on CVM runners.
1100            match backend_hint {
1101                PipelineBackendHint::Ado => {
1102                    filter.push_str(
1103                        " + (test(servicing) & !(test(upgrade) + test(downgrade)) & test(hyperv))",
1104                    );
1105                }
1106                _ => {
1107                    filter.push_str(" + (test(servicing) & test(hyperv))");
1108                }
1109            }
1110
1111            // Exclude any PCAT tests that were picked up by other filters
1112            if isolation_type == "snp" {
1113                filter = format!("({filter}) & !test(pcat)")
1114            }
1115            filter
1116        };
1117        let cvm_x64_test_artifacts = vec![
1118            KnownTestArtifacts::Gen1WindowsDataCenterCore2022X64Vhd,
1119            KnownTestArtifacts::Gen2WindowsDataCenterCore2022X64Vhd,
1120            KnownTestArtifacts::Gen2WindowsDataCenterCore2025X64Vhd,
1121            KnownTestArtifacts::Ubuntu2504ServerX64Vhd,
1122            KnownTestArtifacts::VmgsWith16kTpm,
1123        ];
1124
1125        for VmmTestJobParams {
1126            platform,
1127            arch,
1128            gh_pool,
1129            label,
1130            target,
1131            resolve_vmm_tests_artifacts,
1132            nextest_filter_expr,
1133            test_artifacts,
1134            needs_prep_run,
1135        } in [
1136            VmmTestJobParams {
1137                platform: FlowPlatform::Windows,
1138                arch: FlowArch::X86_64,
1139                gh_pool: crate::pipelines_shared::gh_pools::windows_intel_self_hosted_largedisk(),
1140                label: "x64-windows-intel",
1141                target: CommonTriple::X86_64_WINDOWS_MSVC,
1142                resolve_vmm_tests_artifacts: vmm_tests_artifacts_windows_intel_x86,
1143                nextest_filter_expr: standard_filter.clone(),
1144                test_artifacts: standard_x64_test_artifacts.clone(),
1145                needs_prep_run: false,
1146            },
1147            VmmTestJobParams {
1148                platform: FlowPlatform::Windows,
1149                arch: FlowArch::X86_64,
1150                gh_pool: crate::pipelines_shared::gh_pools::windows_tdx_self_hosted_baremetal(),
1151                label: "x64-windows-intel-tdx",
1152                target: CommonTriple::X86_64_WINDOWS_MSVC,
1153                resolve_vmm_tests_artifacts: vmm_tests_artifacts_windows_intel_tdx_x86,
1154                nextest_filter_expr: cvm_filter("tdx"),
1155                test_artifacts: cvm_x64_test_artifacts.clone(),
1156                needs_prep_run: true,
1157            },
1158            VmmTestJobParams {
1159                platform: FlowPlatform::Windows,
1160                arch: FlowArch::X86_64,
1161                gh_pool: crate::pipelines_shared::gh_pools::windows_amd_self_hosted_largedisk(),
1162                label: "x64-windows-amd",
1163                target: CommonTriple::X86_64_WINDOWS_MSVC,
1164                resolve_vmm_tests_artifacts: vmm_tests_artifacts_windows_amd_x86,
1165                nextest_filter_expr: standard_filter.clone(),
1166                test_artifacts: standard_x64_test_artifacts.clone(),
1167                needs_prep_run: false,
1168            },
1169            VmmTestJobParams {
1170                platform: FlowPlatform::Windows,
1171                arch: FlowArch::X86_64,
1172                gh_pool: crate::pipelines_shared::gh_pools::windows_snp_self_hosted_baremetal(),
1173                label: "x64-windows-amd-snp",
1174                target: CommonTriple::X86_64_WINDOWS_MSVC,
1175                resolve_vmm_tests_artifacts: vmm_tests_artifacts_windows_amd_snp_x86,
1176                nextest_filter_expr: cvm_filter("snp"),
1177                test_artifacts: cvm_x64_test_artifacts,
1178                needs_prep_run: true,
1179            },
1180            VmmTestJobParams {
1181                platform: FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
1182                arch: FlowArch::X86_64,
1183                gh_pool: crate::pipelines_shared::gh_pools::linux_self_hosted_largedisk(),
1184                label: "x64-linux",
1185                target: CommonTriple::X86_64_LINUX_GNU,
1186                resolve_vmm_tests_artifacts: vmm_tests_artifacts_linux_x86,
1187                // - No legal way to obtain gen1 pcat blobs on non-msft linux machines
1188                nextest_filter_expr: format!("{standard_filter} & !test(pcat_x64)"),
1189                test_artifacts: standard_x64_test_artifacts,
1190                needs_prep_run: false,
1191            },
1192            VmmTestJobParams {
1193                platform: FlowPlatform::Windows,
1194                arch: FlowArch::Aarch64,
1195                gh_pool: crate::pipelines_shared::gh_pools::windows_arm_self_hosted_baremetal(),
1196                label: "aarch64-windows",
1197                target: CommonTriple::AARCH64_WINDOWS_MSVC,
1198                resolve_vmm_tests_artifacts: vmm_tests_artifacts_windows_aarch64,
1199                nextest_filter_expr: "all()".to_string(),
1200                test_artifacts: vec![
1201                    KnownTestArtifacts::Alpine323Aarch64Vhd,
1202                    KnownTestArtifacts::Ubuntu2404ServerAarch64Vhd,
1203                    KnownTestArtifacts::Windows11EnterpriseAarch64Vhdx,
1204                    KnownTestArtifacts::VmgsWithBootEntry,
1205                    KnownTestArtifacts::VmgsWith16kTpm,
1206                ],
1207                needs_prep_run: false,
1208            },
1209        ] {
1210            // Skip ARM64/CVM jobs entirely for ADO backend (no native ARM64/CVM pools in ADO)
1211            if matches!(backend_hint, PipelineBackendHint::Ado) {
1212                if matches!(arch, FlowArch::Aarch64)
1213                    || label.contains("tdx")
1214                    || label.contains("snp")
1215                {
1216                    continue;
1217                }
1218            }
1219            let test_label = format!("{label}-vmm-tests");
1220
1221            let pub_vmm_tests_results = if matches!(backend_hint, PipelineBackendHint::Local) {
1222                Some(pipeline.new_artifact(&test_label).0)
1223            } else {
1224                None
1225            };
1226
1227            let use_vmm_tests_archive = match target {
1228                CommonTriple::X86_64_WINDOWS_MSVC => &use_vmm_tests_archive_windows_x86,
1229                CommonTriple::X86_64_LINUX_GNU => &use_vmm_tests_archive_linux_x86,
1230                CommonTriple::AARCH64_WINDOWS_MSVC => &use_vmm_tests_archive_windows_aarch64,
1231                _ => unreachable!(),
1232            };
1233
1234            let mut vmm_tests_run_job = pipeline
1235                .new_job(platform, arch, format!("run vmm-tests [{label}]"))
1236                .gh_set_pool(gh_pool);
1237
1238            // Only add ADO pool for x86_64 jobs (ARM not supported in ADO org)
1239            if matches!(arch, FlowArch::X86_64) {
1240                vmm_tests_run_job = vmm_tests_run_job.ado_set_pool(match platform {
1241                    FlowPlatform::Windows => {
1242                        crate::pipelines_shared::ado_pools::default_x86_pool(FlowPlatform::Windows)
1243                    }
1244                    FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu) => {
1245                        crate::pipelines_shared::ado_pools::default_x86_pool(FlowPlatform::Linux(
1246                            FlowPlatformLinuxDistro::Ubuntu,
1247                        ))
1248                    }
1249                    _ => anyhow::bail!("unsupported platform"),
1250                });
1251            }
1252
1253            vmm_tests_run_job = vmm_tests_run_job.dep_on(|ctx| {
1254                flowey_lib_hvlite::_jobs::consume_and_test_nextest_vmm_tests_archive::Params {
1255                    junit_test_label: test_label,
1256                    nextest_vmm_tests_archive: ctx.use_typed_artifact(use_vmm_tests_archive),
1257                    target: target.as_triple(),
1258                    nextest_profile: flowey_lib_hvlite::run_cargo_nextest_run::NextestProfile::Ci,
1259                    nextest_filter_expr: Some(nextest_filter_expr),
1260                    dep_artifact_dirs: resolve_vmm_tests_artifacts(ctx),
1261                    test_artifacts,
1262                    fail_job_on_test_fail: true,
1263                    artifact_dir: pub_vmm_tests_results.map(|x| ctx.publish_artifact(x)),
1264                    needs_prep_run,
1265                    done: ctx.new_done_handle(),
1266                }
1267            });
1268
1269            if let Some(vmm_tests_disk_cache_dir) = vmm_tests_disk_cache_dir.clone() {
1270                vmm_tests_run_job = vmm_tests_run_job.dep_on(|_| {
1271                    flowey_lib_hvlite::download_openvmm_vmm_tests_artifacts::Request::CustomCacheDir(
1272                        vmm_tests_disk_cache_dir,
1273                    )
1274                })
1275            }
1276
1277            all_jobs.push(vmm_tests_run_job.finish());
1278        }
1279
1280        // test the flowey local backend by running cargo xflowey build-igvm on x64
1281        {
1282            if matches!(backend_hint, PipelineBackendHint::Github) {
1283                let job = pipeline
1284                    .new_job(
1285                        FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
1286                        FlowArch::X86_64,
1287                        "test flowey local backend",
1288                    )
1289                    .gh_set_pool(crate::pipelines_shared::gh_pools::gh_hosted_x64_linux())
1290                    .dep_on(
1291                        |ctx| flowey_lib_hvlite::_jobs::test_local_flowey_build_igvm::Request {
1292                            base_recipe: OpenhclIgvmRecipe::X64,
1293                            done: ctx.new_done_handle(),
1294                        },
1295                    )
1296                    .finish();
1297                all_jobs.push(job);
1298            }
1299        }
1300
1301        if matches!(config, PipelineConfig::Pr)
1302            && matches!(backend_hint, PipelineBackendHint::Github)
1303        {
1304            // Add a job that depends on all others as a workaround for
1305            // https://github.com/orgs/community/discussions/12395.
1306            //
1307            // This workaround then itself requires _another_ workaround, requiring
1308            // the use of `gh_dangerous_override_if`, and some additional custom job
1309            // logic, to deal with https://github.com/actions/runner/issues/2566.
1310            //
1311            // TODO: Add a way for this job to skip flowey setup and become a true
1312            // no-op.
1313            let all_good_job = pipeline
1314                .new_job(
1315                    FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
1316                    FlowArch::X86_64,
1317                    "openvmm checkin gates",
1318                )
1319                .gh_set_pool(crate::pipelines_shared::gh_pools::gh_hosted_x64_linux())
1320                // always run this job, regardless whether or not any previous jobs failed
1321                .gh_dangerous_override_if("always() && github.event.pull_request.draft == false")
1322                .gh_dangerous_global_env_var("ANY_JOBS_FAILED", "${{ contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'failure') }}")
1323                .dep_on(|ctx| flowey_lib_hvlite::_jobs::all_good_job::Params {
1324                    did_fail_env_var: "ANY_JOBS_FAILED".into(),
1325                    done: ctx.new_done_handle(),
1326                })
1327                .finish();
1328
1329            for job in all_jobs.iter() {
1330                pipeline.non_artifact_dep(&all_good_job, job);
1331            }
1332        }
1333
1334        if matches!(config, PipelineConfig::Ci)
1335            && matches!(backend_hint, PipelineBackendHint::Github)
1336        {
1337            let publish_vmgstool_job = pipeline
1338                .new_job(
1339                    FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
1340                    FlowArch::X86_64,
1341                    "publish vmgstool",
1342                )
1343                .gh_grant_permissions::<flowey_lib_common::publish_gh_release::Node>([(
1344                    GhPermission::Contents,
1345                    GhPermissionValue::Write,
1346                )])
1347                .gh_set_pool(crate::pipelines_shared::gh_pools::gh_hosted_x64_linux())
1348                .dep_on(
1349                    |ctx| flowey_lib_hvlite::_jobs::publish_vmgstool_gh_release::Request {
1350                        vmgstools: vmgstools
1351                            .into_iter()
1352                            .map(|(t, v)| (t, ctx.use_typed_artifact(&v)))
1353                            .collect(),
1354                        done: ctx.new_done_handle(),
1355                    },
1356                )
1357                .finish();
1358
1359            // All other jobs must succeed in order to publish
1360            for job in all_jobs.iter() {
1361                pipeline.non_artifact_dep(&publish_vmgstool_job, job);
1362            }
1363        }
1364
1365        Ok(pipeline)
1366    }
1367}
1368
1369/// Utility builders which make it easy to "skim off" artifacts required by VMM
1370/// test execution from other pipeline jobs.
1371//
1372// FUTURE: if we end up having a _lot_ of VMM test jobs, this would be the sort
1373// of thing that would really benefit from a derive macro.
1374mod vmm_tests_artifact_builders {
1375    use flowey::pipeline::prelude::*;
1376    use flowey_lib_hvlite::_jobs::consume_and_test_nextest_vmm_tests_archive::VmmTestsDepArtifacts;
1377    use flowey_lib_hvlite::build_guest_test_uefi::GuestTestUefiOutput;
1378    use flowey_lib_hvlite::build_openvmm::OpenvmmOutput;
1379    use flowey_lib_hvlite::build_pipette::PipetteOutput;
1380    use flowey_lib_hvlite::build_prep_steps::PrepStepsOutput;
1381    use flowey_lib_hvlite::build_test_igvm_agent_rpc_server::TestIgvmAgentRpcServerOutput;
1382    use flowey_lib_hvlite::build_tmk_vmm::TmkVmmOutput;
1383    use flowey_lib_hvlite::build_tmks::TmksOutput;
1384    use flowey_lib_hvlite::build_tpm_guest_tests::TpmGuestTestsOutput;
1385    use flowey_lib_hvlite::build_vmgstool::VmgstoolOutput;
1386
1387    pub type ResolveVmmTestsDepArtifacts =
1388        Box<dyn Fn(&mut PipelineJobCtx<'_>) -> VmmTestsDepArtifacts>;
1389
1390    #[derive(Default)]
1391    pub struct VmmTestsArtifactsBuilderLinuxX86 {
1392        // windows build machine
1393        pub use_pipette_windows: Option<UseTypedArtifact<PipetteOutput>>,
1394        pub use_tmk_vmm: Option<UseTypedArtifact<TmkVmmOutput>>,
1395        // linux build machine
1396        pub use_openvmm: Option<UseTypedArtifact<OpenvmmOutput>>,
1397        pub use_pipette_linux_musl: Option<UseTypedArtifact<PipetteOutput>>,
1398        // any machine
1399        pub use_guest_test_uefi: Option<UseTypedArtifact<GuestTestUefiOutput>>,
1400        pub use_tmks: Option<UseTypedArtifact<TmksOutput>>,
1401    }
1402
1403    impl VmmTestsArtifactsBuilderLinuxX86 {
1404        pub fn finish(self) -> Result<ResolveVmmTestsDepArtifacts, &'static str> {
1405            let VmmTestsArtifactsBuilderLinuxX86 {
1406                use_openvmm,
1407                use_guest_test_uefi,
1408                use_pipette_windows,
1409                use_pipette_linux_musl,
1410                use_tmk_vmm,
1411                use_tmks,
1412            } = self;
1413
1414            let use_guest_test_uefi = use_guest_test_uefi.ok_or("guest_test_uefi")?;
1415            let use_openvmm = use_openvmm.ok_or("openvmm")?;
1416            let use_pipette_linux_musl = use_pipette_linux_musl.ok_or("pipette_linux_musl")?;
1417            let use_pipette_windows = use_pipette_windows.ok_or("pipette_windows")?;
1418            let use_tmk_vmm = use_tmk_vmm.ok_or("tmk_vmm")?;
1419            let use_tmks = use_tmks.ok_or("tmks")?;
1420
1421            Ok(Box::new(move |ctx| VmmTestsDepArtifacts {
1422                openvmm: Some(ctx.use_typed_artifact(&use_openvmm)),
1423                pipette_windows: Some(ctx.use_typed_artifact(&use_pipette_windows)),
1424                pipette_linux_musl: Some(ctx.use_typed_artifact(&use_pipette_linux_musl)),
1425                guest_test_uefi: Some(ctx.use_typed_artifact(&use_guest_test_uefi)),
1426                tmk_vmm: Some(ctx.use_typed_artifact(&use_tmk_vmm)),
1427                tmks: Some(ctx.use_typed_artifact(&use_tmks)),
1428                // not currently required, since OpenHCL tests cannot be run on OpenVMM on linux
1429                artifact_dir_openhcl_igvm_files: None,
1430                tmk_vmm_linux_musl: None,
1431                prep_steps: None,
1432                vmgstool: None,
1433                tpm_guest_tests_windows: None,
1434                tpm_guest_tests_linux: None,
1435                test_igvm_agent_rpc_server: None,
1436            }))
1437        }
1438    }
1439
1440    #[derive(Default, Clone)]
1441    pub struct VmmTestsArtifactsBuilderWindowsX86 {
1442        // windows build machine
1443        pub use_openvmm: Option<UseTypedArtifact<OpenvmmOutput>>,
1444        pub use_pipette_windows: Option<UseTypedArtifact<PipetteOutput>>,
1445        pub use_tmk_vmm: Option<UseTypedArtifact<TmkVmmOutput>>,
1446        pub use_prep_steps: Option<UseTypedArtifact<PrepStepsOutput>>,
1447        pub use_vmgstool: Option<UseTypedArtifact<VmgstoolOutput>>,
1448        pub use_tpm_guest_tests_windows: Option<UseTypedArtifact<TpmGuestTestsOutput>>,
1449        pub use_tpm_guest_tests_linux: Option<UseTypedArtifact<TpmGuestTestsOutput>>,
1450        pub use_test_igvm_agent_rpc_server: Option<UseTypedArtifact<TestIgvmAgentRpcServerOutput>>,
1451        // linux build machine
1452        pub use_openhcl_igvm_files: Option<UseArtifact>,
1453        pub use_pipette_linux_musl: Option<UseTypedArtifact<PipetteOutput>>,
1454        pub use_tmk_vmm_linux_musl: Option<UseTypedArtifact<TmkVmmOutput>>,
1455        // any machine
1456        pub use_guest_test_uefi: Option<UseTypedArtifact<GuestTestUefiOutput>>,
1457        pub use_tmks: Option<UseTypedArtifact<TmksOutput>>,
1458    }
1459
1460    impl VmmTestsArtifactsBuilderWindowsX86 {
1461        pub fn finish(self) -> Result<ResolveVmmTestsDepArtifacts, &'static str> {
1462            let VmmTestsArtifactsBuilderWindowsX86 {
1463                use_openvmm,
1464                use_pipette_windows,
1465                use_pipette_linux_musl,
1466                use_guest_test_uefi,
1467                use_openhcl_igvm_files,
1468                use_tmk_vmm,
1469                use_tmk_vmm_linux_musl,
1470                use_tmks,
1471                use_prep_steps,
1472                use_vmgstool,
1473                use_tpm_guest_tests_windows,
1474                use_tpm_guest_tests_linux,
1475                use_test_igvm_agent_rpc_server,
1476            } = self;
1477
1478            let use_openvmm = use_openvmm.ok_or("openvmm")?;
1479            let use_pipette_windows = use_pipette_windows.ok_or("pipette_windows")?;
1480            let use_pipette_linux_musl = use_pipette_linux_musl.ok_or("pipette_linux_musl")?;
1481            let use_guest_test_uefi = use_guest_test_uefi.ok_or("guest_test_uefi")?;
1482            let use_openhcl_igvm_files = use_openhcl_igvm_files.ok_or("openhcl_igvm_files")?;
1483            let use_tmk_vmm = use_tmk_vmm.ok_or("tmk_vmm")?;
1484            let use_tmk_vmm_linux_musl = use_tmk_vmm_linux_musl.ok_or("tmk_vmm_linux_musl")?;
1485            let use_tmks = use_tmks.ok_or("tmks")?;
1486            let use_prep_steps = use_prep_steps.ok_or("prep_steps")?;
1487            let use_vmgstool = use_vmgstool.ok_or("vmgstool")?;
1488            let use_tpm_guest_tests_windows =
1489                use_tpm_guest_tests_windows.ok_or("tpm_guest_tests_windows")?;
1490            let use_tpm_guest_tests_linux =
1491                use_tpm_guest_tests_linux.ok_or("tpm_guest_tests_linux")?;
1492            let use_test_igvm_agent_rpc_server =
1493                use_test_igvm_agent_rpc_server.ok_or("test_igvm_agent_rpc_server")?;
1494
1495            Ok(Box::new(move |ctx| VmmTestsDepArtifacts {
1496                openvmm: Some(ctx.use_typed_artifact(&use_openvmm)),
1497                pipette_windows: Some(ctx.use_typed_artifact(&use_pipette_windows)),
1498                pipette_linux_musl: Some(ctx.use_typed_artifact(&use_pipette_linux_musl)),
1499                guest_test_uefi: Some(ctx.use_typed_artifact(&use_guest_test_uefi)),
1500                artifact_dir_openhcl_igvm_files: Some(ctx.use_artifact(&use_openhcl_igvm_files)),
1501                tmk_vmm: Some(ctx.use_typed_artifact(&use_tmk_vmm)),
1502                tmk_vmm_linux_musl: Some(ctx.use_typed_artifact(&use_tmk_vmm_linux_musl)),
1503                tmks: Some(ctx.use_typed_artifact(&use_tmks)),
1504                prep_steps: Some(ctx.use_typed_artifact(&use_prep_steps)),
1505                vmgstool: Some(ctx.use_typed_artifact(&use_vmgstool)),
1506                tpm_guest_tests_windows: Some(ctx.use_typed_artifact(&use_tpm_guest_tests_windows)),
1507                tpm_guest_tests_linux: Some(ctx.use_typed_artifact(&use_tpm_guest_tests_linux)),
1508                test_igvm_agent_rpc_server: Some(
1509                    ctx.use_typed_artifact(&use_test_igvm_agent_rpc_server),
1510                ),
1511            }))
1512        }
1513    }
1514
1515    #[derive(Default, Clone)]
1516    pub struct VmmTestsArtifactsBuilderWindowsAarch64 {
1517        // windows build machine
1518        pub use_openvmm: Option<UseTypedArtifact<OpenvmmOutput>>,
1519        pub use_pipette_windows: Option<UseTypedArtifact<PipetteOutput>>,
1520        pub use_tmk_vmm: Option<UseTypedArtifact<TmkVmmOutput>>,
1521        pub use_vmgstool: Option<UseTypedArtifact<VmgstoolOutput>>,
1522        // linux build machine
1523        pub use_openhcl_igvm_files: Option<UseArtifact>,
1524        pub use_pipette_linux_musl: Option<UseTypedArtifact<PipetteOutput>>,
1525        pub use_tmk_vmm_linux_musl: Option<UseTypedArtifact<TmkVmmOutput>>,
1526        // any machine
1527        pub use_guest_test_uefi: Option<UseTypedArtifact<GuestTestUefiOutput>>,
1528        pub use_tmks: Option<UseTypedArtifact<TmksOutput>>,
1529    }
1530
1531    impl VmmTestsArtifactsBuilderWindowsAarch64 {
1532        pub fn finish(self) -> Result<ResolveVmmTestsDepArtifacts, &'static str> {
1533            let VmmTestsArtifactsBuilderWindowsAarch64 {
1534                use_openvmm,
1535                use_pipette_windows,
1536                use_pipette_linux_musl,
1537                use_guest_test_uefi,
1538                use_openhcl_igvm_files,
1539                use_tmk_vmm,
1540                use_tmk_vmm_linux_musl,
1541                use_tmks,
1542                use_vmgstool,
1543            } = self;
1544
1545            let use_openvmm = use_openvmm.ok_or("openvmm")?;
1546            let use_pipette_windows = use_pipette_windows.ok_or("pipette_windows")?;
1547            let use_pipette_linux_musl = use_pipette_linux_musl.ok_or("pipette_linux_musl")?;
1548            let use_guest_test_uefi = use_guest_test_uefi.ok_or("guest_test_uefi")?;
1549            let use_openhcl_igvm_files = use_openhcl_igvm_files.ok_or("openhcl_igvm_files")?;
1550            let use_tmk_vmm = use_tmk_vmm.ok_or("tmk_vmm")?;
1551            let use_tmk_vmm_linux_musl = use_tmk_vmm_linux_musl.ok_or("tmk_vmm_linux_musl")?;
1552            let use_tmks = use_tmks.ok_or("tmks")?;
1553            let use_vmgstool = use_vmgstool.ok_or("vmgstool")?;
1554
1555            Ok(Box::new(move |ctx| VmmTestsDepArtifacts {
1556                openvmm: Some(ctx.use_typed_artifact(&use_openvmm)),
1557                pipette_windows: Some(ctx.use_typed_artifact(&use_pipette_windows)),
1558                pipette_linux_musl: Some(ctx.use_typed_artifact(&use_pipette_linux_musl)),
1559                guest_test_uefi: Some(ctx.use_typed_artifact(&use_guest_test_uefi)),
1560                artifact_dir_openhcl_igvm_files: Some(ctx.use_artifact(&use_openhcl_igvm_files)),
1561                tmk_vmm: Some(ctx.use_typed_artifact(&use_tmk_vmm)),
1562                tmk_vmm_linux_musl: Some(ctx.use_typed_artifact(&use_tmk_vmm_linux_musl)),
1563                tmks: Some(ctx.use_typed_artifact(&use_tmks)),
1564                prep_steps: None,
1565                vmgstool: Some(ctx.use_typed_artifact(&use_vmgstool)),
1566                tpm_guest_tests_windows: None,
1567                tpm_guest_tests_linux: None,
1568                test_igvm_agent_rpc_server: None,
1569            }))
1570        }
1571    }
1572}