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