Skip to main content

flowey_hvlite/pipelines/
checkin_gates.rs

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