flowey_hvlite/pipelines/
checkin_gates.rs

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