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            // filter off interesting artifacts required by the VMM tests job
256            match arch {
257                CommonArch::X86_64 => {
258                    vmm_tests_artifacts_linux_x86.use_pipette_windows =
259                        Some(use_pipette_windows.clone());
260                    vmm_tests_artifacts_windows_x86.use_openvmm = Some(use_openvmm.clone());
261                    vmm_tests_artifacts_windows_x86.use_pipette_windows =
262                        Some(use_pipette_windows.clone());
263                    vmm_tests_artifacts_windows_x86.use_tmk_vmm = Some(use_tmk_vmm.clone());
264                }
265                CommonArch::Aarch64 => {
266                    vmm_tests_artifacts_windows_aarch64.use_openvmm = Some(use_openvmm.clone());
267                    vmm_tests_artifacts_windows_aarch64.use_pipette_windows =
268                        Some(use_pipette_windows.clone());
269                    vmm_tests_artifacts_windows_aarch64.use_tmk_vmm = Some(use_tmk_vmm.clone());
270                }
271            }
272            // emit a job for artifacts which _are not_ in the VMM tests "hot
273            // path"
274            // artifacts which _are not_ in the VMM tests "hot path"
275            let (pub_igvmfilegen, _use_igvmfilegen) =
276                pipeline.new_typed_artifact(format!("{arch_tag}-windows-igvmfilegen"));
277            let (pub_vmgs_lib, _use_vmgs_lib) =
278                pipeline.new_typed_artifact(format!("{arch_tag}-windows-vmgs_lib"));
279            let (pub_vmgstool, _use_vmgstool) =
280                pipeline.new_typed_artifact(format!("{arch_tag}-windows-vmgstool"));
281            let (pub_hypestv, _use_hypestv) =
282                pipeline.new_typed_artifact(format!("{arch_tag}-windows-hypestv"));
283            let (pub_ohcldiag_dev, _use_ohcldiag_dev) =
284                pipeline.new_typed_artifact(format!("{arch_tag}-windows-ohcldiag-dev"));
285
286            let job = pipeline
287                .new_job(
288                    FlowPlatform::Windows,
289                    FlowArch::X86_64,
290                    format!("build artifacts (not for VMM tests) [{arch_tag}-windows]"),
291                )
292                .gh_set_pool(crate::pipelines_shared::gh_pools::default_x86_pool(
293                    FlowPlatform::Windows,
294                ))
295                .dep_on(|ctx| flowey_lib_hvlite::build_vmgstool::Request {
296                    target: CommonTriple::Common {
297                        arch,
298                        platform: CommonPlatform::WindowsMsvc,
299                    },
300                    profile: CommonProfile::from_release(release),
301                    with_crypto: true,
302                    vmgstool: ctx.publish_typed_artifact(pub_vmgstool),
303                })
304                .dep_on(|ctx| flowey_lib_hvlite::build_hypestv::Request {
305                    target: CommonTriple::Common {
306                        arch,
307                        platform: CommonPlatform::WindowsMsvc,
308                    },
309                    profile: CommonProfile::from_release(release),
310                    hypestv: ctx.publish_typed_artifact(pub_hypestv),
311                })
312                .dep_on(|ctx| flowey_lib_hvlite::build_and_test_vmgs_lib::Request {
313                    target: CommonTriple::Common {
314                        arch,
315                        platform: CommonPlatform::WindowsMsvc,
316                    },
317                    profile: CommonProfile::from_release(release),
318                    vmgs_lib: ctx.publish_typed_artifact(pub_vmgs_lib),
319                })
320                .dep_on(|ctx| flowey_lib_hvlite::build_igvmfilegen::Request {
321                    build_params: flowey_lib_hvlite::build_igvmfilegen::IgvmfilegenBuildParams {
322                        target: CommonTriple::Common {
323                            arch,
324                            platform: CommonPlatform::WindowsMsvc,
325                        },
326                        profile: CommonProfile::from_release(release).into(),
327                    },
328                    igvmfilegen: ctx.publish_typed_artifact(pub_igvmfilegen),
329                })
330                .dep_on(|ctx| flowey_lib_hvlite::build_ohcldiag_dev::Request {
331                    target: CommonTriple::Common {
332                        arch,
333                        platform: CommonPlatform::WindowsMsvc,
334                    },
335                    profile: CommonProfile::from_release(release),
336                    ohcldiag_dev: ctx.publish_typed_artifact(pub_ohcldiag_dev),
337                });
338
339            all_jobs.push(job.finish());
340
341            // emit a job for artifacts which _are_ in the VMM tests "hot path"
342            let mut job = pipeline
343                .new_job(
344                    FlowPlatform::Windows,
345                    FlowArch::X86_64,
346                    format!("build artifacts (for VMM tests) [{arch_tag}-windows]"),
347                )
348                .gh_set_pool(crate::pipelines_shared::gh_pools::default_gh_hosted(
349                    FlowPlatform::Windows,
350                ))
351                .dep_on(|ctx| {
352                    flowey_lib_hvlite::build_openvmm::Request {
353                        params: flowey_lib_hvlite::build_openvmm::OpenvmmBuildParams {
354                            target: CommonTriple::Common {
355                                arch,
356                                platform: CommonPlatform::WindowsMsvc,
357                            },
358                            profile: CommonProfile::from_release(release),
359                            // FIXME: this relies on openvmm default features
360                            // Our ARM test runners need the latest WHP changes
361                            features: if matches!(arch, CommonArch::Aarch64) {
362                                [flowey_lib_hvlite::build_openvmm::OpenvmmFeature::UnstableWhp]
363                                    .into()
364                            } else {
365                                [].into()
366                            },
367                        },
368                        openvmm: ctx.publish_typed_artifact(pub_openvmm),
369                    }
370                })
371                .dep_on(|ctx| flowey_lib_hvlite::build_pipette::Request {
372                    target: CommonTriple::Common {
373                        arch,
374                        platform: CommonPlatform::WindowsMsvc,
375                    },
376                    profile: CommonProfile::from_release(release),
377                    pipette: ctx.publish_typed_artifact(pub_pipette_windows),
378                })
379                .dep_on(|ctx| flowey_lib_hvlite::build_tmk_vmm::Request {
380                    target: CommonTriple::Common {
381                        arch,
382                        platform: CommonPlatform::WindowsMsvc,
383                    },
384                    unstable_whp: true, // The ARM64 CI runner supports the unstable WHP interface
385                    profile: CommonProfile::from_release(release),
386                    tmk_vmm: ctx.publish_typed_artifact(pub_tmk_vmm),
387                });
388
389            // Hang building the windows VMM tests off this big windows job.
390            match arch {
391                CommonArch::X86_64 => {
392                    let pub_vmm_tests_archive_windows_x86 =
393                        pub_vmm_tests_archive_windows_x86.take().unwrap();
394                    job = job.dep_on(|ctx|
395                        flowey_lib_hvlite::build_nextest_vmm_tests::Request {
396                        target: CommonTriple::X86_64_WINDOWS_MSVC.as_triple(),
397                        profile: CommonProfile::from_release(release),
398                        build_mode: flowey_lib_hvlite::build_nextest_vmm_tests::BuildNextestVmmTestsMode::Archive(
399                            ctx.publish_typed_artifact(pub_vmm_tests_archive_windows_x86),
400                        ),
401                    });
402                }
403                CommonArch::Aarch64 => {
404                    let pub_vmm_tests_archive_windows_aarch64 =
405                        pub_vmm_tests_archive_windows_aarch64.take().unwrap();
406                    job = job.dep_on(|ctx| flowey_lib_hvlite::build_nextest_vmm_tests::Request {
407                        target: CommonTriple::AARCH64_WINDOWS_MSVC.as_triple(),
408                        profile: CommonProfile::from_release(release),
409                        build_mode: flowey_lib_hvlite::build_nextest_vmm_tests::BuildNextestVmmTestsMode::Archive(
410                            ctx.publish_typed_artifact(pub_vmm_tests_archive_windows_aarch64),
411                        ),
412                    });
413                }
414            }
415
416            all_jobs.push(job.finish());
417        }
418
419        // emit linux build machine jobs (without openhcl)
420        for arch in [CommonArch::Aarch64, CommonArch::X86_64] {
421            let arch_tag = match arch {
422                CommonArch::X86_64 => "x64",
423                CommonArch::Aarch64 => "aarch64",
424            };
425
426            let (pub_openvmm, use_openvmm) =
427                pipeline.new_typed_artifact(format!("{arch_tag}-linux-openvmm"));
428            let (pub_igvmfilegen, _) =
429                pipeline.new_typed_artifact(format!("{arch_tag}-linux-igvmfilegen"));
430            let (pub_vmgs_lib, _) =
431                pipeline.new_typed_artifact(format!("{arch_tag}-linux-vmgs_lib"));
432            let (pub_vmgstool, _) =
433                pipeline.new_typed_artifact(format!("{arch_tag}-linux-vmgstool"));
434            let (pub_ohcldiag_dev, _) =
435                pipeline.new_typed_artifact(format!("{arch_tag}-linux-ohcldiag-dev"));
436            let (pub_tmks, use_tmks) = pipeline.new_typed_artifact(format!("{arch_tag}-tmks"));
437
438            // NOTE: the choice to build it as part of this linux job was pretty
439            // arbitrary. It could just as well hang off the windows job.
440            //
441            // At this time though, having it here results in a net-reduction in
442            // E2E pipeline times, owing to how the VMM tests artifact dependency
443            // graph looks like.
444            let (pub_guest_test_uefi, use_guest_test_uefi) =
445                pipeline.new_typed_artifact(format!("{arch_tag}-guest_test_uefi"));
446
447            // skim off interesting artifacts required by the VMM tests job
448            match arch {
449                CommonArch::X86_64 => {
450                    vmm_tests_artifacts_linux_x86.use_openvmm = Some(use_openvmm.clone());
451                    vmm_tests_artifacts_linux_x86.use_guest_test_uefi =
452                        Some(use_guest_test_uefi.clone());
453                    vmm_tests_artifacts_windows_x86.use_guest_test_uefi =
454                        Some(use_guest_test_uefi.clone());
455                    vmm_tests_artifacts_windows_x86.use_tmks = Some(use_tmks.clone());
456                    vmm_tests_artifacts_linux_x86.use_tmks = Some(use_tmks.clone());
457                }
458                CommonArch::Aarch64 => {
459                    vmm_tests_artifacts_windows_aarch64.use_guest_test_uefi =
460                        Some(use_guest_test_uefi.clone());
461                    vmm_tests_artifacts_windows_aarch64.use_tmks = Some(use_tmks.clone());
462                }
463            }
464
465            let mut job = pipeline
466                .new_job(
467                    FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
468                    FlowArch::X86_64,
469                    format!("build artifacts [{arch_tag}-linux]"),
470                )
471                .gh_set_pool(crate::pipelines_shared::gh_pools::default_x86_pool(
472                    FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
473                ))
474                .dep_on(|ctx| {
475                    flowey_lib_hvlite::build_openvmm::Request {
476                        params: flowey_lib_hvlite::build_openvmm::OpenvmmBuildParams {
477                            target: CommonTriple::Common {
478                                arch,
479                                platform: CommonPlatform::LinuxGnu,
480                            },
481                            profile: CommonProfile::from_release(release),
482                            // FIXME: this relies on openvmm default features
483                            features: [flowey_lib_hvlite::build_openvmm::OpenvmmFeature::Tpm]
484                                .into(),
485                        },
486                        openvmm: ctx.publish_typed_artifact(pub_openvmm),
487                    }
488                })
489                .dep_on(|ctx| flowey_lib_hvlite::build_vmgstool::Request {
490                    target: CommonTriple::Common {
491                        arch,
492                        platform: CommonPlatform::LinuxGnu,
493                    },
494                    profile: CommonProfile::from_release(release),
495                    with_crypto: true,
496                    vmgstool: ctx.publish_typed_artifact(pub_vmgstool),
497                })
498                .dep_on(|ctx| flowey_lib_hvlite::build_and_test_vmgs_lib::Request {
499                    target: CommonTriple::Common {
500                        arch,
501                        platform: CommonPlatform::LinuxGnu,
502                    },
503                    profile: CommonProfile::from_release(release),
504                    vmgs_lib: ctx.publish_typed_artifact(pub_vmgs_lib),
505                })
506                .dep_on(|ctx| flowey_lib_hvlite::build_igvmfilegen::Request {
507                    build_params: flowey_lib_hvlite::build_igvmfilegen::IgvmfilegenBuildParams {
508                        target: CommonTriple::Common {
509                            arch,
510                            platform: CommonPlatform::LinuxGnu,
511                        },
512                        profile: CommonProfile::from_release(release).into(),
513                    },
514                    igvmfilegen: ctx.publish_typed_artifact(pub_igvmfilegen),
515                })
516                .dep_on(|ctx| flowey_lib_hvlite::build_ohcldiag_dev::Request {
517                    target: CommonTriple::Common {
518                        arch,
519                        platform: CommonPlatform::LinuxGnu,
520                    },
521                    profile: CommonProfile::from_release(release),
522                    ohcldiag_dev: ctx.publish_typed_artifact(pub_ohcldiag_dev),
523                })
524                .dep_on(|ctx| flowey_lib_hvlite::build_guest_test_uefi::Request {
525                    arch,
526                    profile: CommonProfile::from_release(release),
527                    guest_test_uefi: ctx.publish_typed_artifact(pub_guest_test_uefi),
528                })
529                .dep_on(|ctx| flowey_lib_hvlite::build_tmks::Request {
530                    arch,
531                    profile: CommonProfile::from_release(release),
532                    tmks: ctx.publish_typed_artifact(pub_tmks),
533                });
534
535            // Hang building the linux VMM tests off this big linux job.
536            //
537            // No ARM64 VMM tests yet
538            if matches!(arch, CommonArch::X86_64) {
539                let pub_vmm_tests_archive_linux_x86 =
540                    pub_vmm_tests_archive_linux_x86.take().unwrap();
541                job = job.dep_on(|ctx| flowey_lib_hvlite::build_nextest_vmm_tests::Request {
542                    target: CommonTriple::X86_64_LINUX_GNU.as_triple(),
543                    profile: CommonProfile::from_release(release),
544                    build_mode: flowey_lib_hvlite::build_nextest_vmm_tests::BuildNextestVmmTestsMode::Archive(
545                        ctx.publish_typed_artifact(pub_vmm_tests_archive_linux_x86),
546                    ),
547                });
548            }
549
550            all_jobs.push(job.finish());
551        }
552
553        // emit openhcl build job
554        for arch in [CommonArch::Aarch64, CommonArch::X86_64] {
555            let arch_tag = match arch {
556                CommonArch::X86_64 => "x64",
557                CommonArch::Aarch64 => "aarch64",
558            };
559
560            let openvmm_hcl_profile = if release {
561                OpenvmmHclBuildProfile::OpenvmmHclShip
562            } else {
563                OpenvmmHclBuildProfile::Debug
564            };
565
566            let (pub_openhcl_igvm, use_openhcl_igvm) =
567                pipeline.new_artifact(format!("{arch_tag}-openhcl-igvm"));
568            let (pub_openhcl_igvm_extras, _use_openhcl_igvm_extras) =
569                pipeline.new_artifact(format!("{arch_tag}-openhcl-igvm-extras"));
570
571            let (pub_openhcl_baseline, _use_openhcl_baseline) =
572                if matches!(config, PipelineConfig::Ci) {
573                    let (p, u) = pipeline.new_artifact(format!("{arch_tag}-openhcl-baseline"));
574                    (Some(p), Some(u))
575                } else {
576                    (None, None)
577                };
578
579            // also build pipette musl on this job, as until we land the
580            // refactor that allows building musl without the full openhcl
581            // toolchain, it would require pulling in all the openhcl
582            // toolchain deps...
583            let (pub_pipette_linux_musl, use_pipette_linux_musl) =
584                pipeline.new_typed_artifact(format!("{arch_tag}-linux-musl-pipette"));
585
586            let (pub_tmk_vmm, use_tmk_vmm) =
587                pipeline.new_typed_artifact(format!("{arch_tag}-linux-musl-tmk_vmm"));
588
589            // skim off interesting artifacts required by the VMM tests job
590            match arch {
591                CommonArch::X86_64 => {
592                    vmm_tests_artifacts_windows_x86.use_openhcl_igvm_files =
593                        Some(use_openhcl_igvm.clone());
594                    vmm_tests_artifacts_windows_x86.use_pipette_linux_musl =
595                        Some(use_pipette_linux_musl.clone());
596                    vmm_tests_artifacts_linux_x86.use_pipette_linux_musl =
597                        Some(use_pipette_linux_musl.clone());
598                    vmm_tests_artifacts_linux_x86.use_tmk_vmm = Some(use_tmk_vmm.clone());
599                    vmm_tests_artifacts_windows_x86.use_tmk_vmm_linux_musl =
600                        Some(use_tmk_vmm.clone());
601                }
602                CommonArch::Aarch64 => {
603                    vmm_tests_artifacts_windows_aarch64.use_openhcl_igvm_files =
604                        Some(use_openhcl_igvm.clone());
605                    vmm_tests_artifacts_windows_aarch64.use_pipette_linux_musl =
606                        Some(use_pipette_linux_musl.clone());
607                    vmm_tests_artifacts_windows_aarch64.use_tmk_vmm_linux_musl =
608                        Some(use_tmk_vmm.clone());
609                }
610            }
611            let igvm_recipes = match arch {
612                CommonArch::X86_64 => vec![
613                    OpenhclIgvmRecipe::X64,
614                    OpenhclIgvmRecipe::X64Devkern,
615                    OpenhclIgvmRecipe::X64TestLinuxDirect,
616                    OpenhclIgvmRecipe::X64TestLinuxDirectDevkern,
617                    OpenhclIgvmRecipe::X64Cvm,
618                ],
619                CommonArch::Aarch64 => {
620                    vec![
621                        OpenhclIgvmRecipe::Aarch64,
622                        OpenhclIgvmRecipe::Aarch64Devkern,
623                    ]
624                }
625            };
626
627            let job = pipeline
628                .new_job(
629                    FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
630                    FlowArch::X86_64,
631                    format!("build openhcl [{arch_tag}-linux]"),
632                )
633                .gh_set_pool(crate::pipelines_shared::gh_pools::default_x86_pool(
634                    FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
635                ))
636                .dep_on(|ctx| {
637                    let publish_baseline_artifact = pub_openhcl_baseline
638                        .map(|baseline_artifact| ctx.publish_artifact(baseline_artifact));
639
640                    flowey_lib_hvlite::_jobs::build_and_publish_openhcl_igvm_from_recipe::Params {
641                        igvm_files: igvm_recipes
642                            .clone()
643                            .into_iter()
644                            .map(|recipe| OpenhclIgvmBuildParams {
645                                profile: openvmm_hcl_profile,
646                                recipe,
647                                custom_target: Some(CommonTriple::Custom(openhcl_musl_target(
648                                    arch,
649                                ))),
650                            })
651                            .collect(),
652                        artifact_dir_openhcl_igvm: ctx.publish_artifact(pub_openhcl_igvm),
653                        artifact_dir_openhcl_igvm_extras: ctx
654                            .publish_artifact(pub_openhcl_igvm_extras),
655                        artifact_openhcl_verify_size_baseline: publish_baseline_artifact,
656                        done: ctx.new_done_handle(),
657                    }
658                })
659                .dep_on(|ctx| flowey_lib_hvlite::build_pipette::Request {
660                    target: CommonTriple::Common {
661                        arch,
662                        platform: CommonPlatform::LinuxMusl,
663                    },
664                    profile: CommonProfile::from_release(release),
665                    pipette: ctx.publish_typed_artifact(pub_pipette_linux_musl),
666                })
667                .dep_on(|ctx| flowey_lib_hvlite::build_tmk_vmm::Request {
668                    target: CommonTriple::Common {
669                        arch,
670                        platform: CommonPlatform::LinuxMusl,
671                    },
672                    profile: CommonProfile::from_release(release),
673                    unstable_whp: false,
674                    tmk_vmm: ctx.publish_typed_artifact(pub_tmk_vmm),
675                });
676
677            all_jobs.push(job.finish());
678
679            if arch == CommonArch::X86_64 && matches!(config, PipelineConfig::Pr) {
680                let job = pipeline
681                    .new_job(
682                        FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
683                        FlowArch::X86_64,
684                        format!("verify openhcl binary size [{}]", arch_tag),
685                    )
686                    .gh_set_pool(crate::pipelines_shared::gh_pools::default_x86_pool(
687                        FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
688                    ))
689                    .dep_on(
690                        |ctx| flowey_lib_hvlite::_jobs::check_openvmm_hcl_size::Request {
691                            target: CommonTriple::Common {
692                                arch,
693                                platform: CommonPlatform::LinuxMusl,
694                            },
695                            done: ctx.new_done_handle(),
696                            pipeline_name: "openvmm-ci.yaml".into(),
697                        },
698                    )
699                    .finish();
700                all_jobs.push(job);
701            }
702        }
703
704        // Emit clippy + unit-test jobs
705        //
706        // The only reason we bundle clippy and unit-tests together is to avoid
707        // requiring another build agent.
708        struct ClippyUnitTestJobParams<'a> {
709            platform: FlowPlatform,
710            arch: FlowArch,
711            gh_pool: GhRunner,
712            clippy_targets: Option<(&'a str, &'a [(Triple, bool)])>,
713            unit_test_target: Option<(&'a str, Triple)>,
714        }
715
716        for ClippyUnitTestJobParams {
717            platform,
718            arch,
719            gh_pool,
720            clippy_targets,
721            unit_test_target,
722        } in [
723            ClippyUnitTestJobParams {
724                platform: FlowPlatform::Windows,
725                arch: FlowArch::X86_64,
726                gh_pool: crate::pipelines_shared::gh_pools::windows_amd_self_hosted(),
727                clippy_targets: Some((
728                    "windows",
729                    &[
730                        (target_lexicon::triple!("x86_64-pc-windows-msvc"), false),
731                        (target_lexicon::triple!("aarch64-pc-windows-msvc"), false),
732                    ],
733                )),
734                unit_test_target: Some((
735                    "x64-windows",
736                    target_lexicon::triple!("x86_64-pc-windows-msvc"),
737                )),
738            },
739            ClippyUnitTestJobParams {
740                platform: FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
741                arch: FlowArch::X86_64,
742                gh_pool: crate::pipelines_shared::gh_pools::linux_self_hosted(),
743                clippy_targets: Some((
744                    "linux, macos",
745                    &[
746                        (target_lexicon::triple!("x86_64-unknown-linux-gnu"), false),
747                        (target_lexicon::triple!("aarch64-unknown-linux-gnu"), false),
748                        (target_lexicon::triple!("aarch64-apple-darwin"), false),
749                    ],
750                )),
751                unit_test_target: Some((
752                    "x64-linux",
753                    target_lexicon::triple!("x86_64-unknown-linux-gnu"),
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-musl, misc nostd",
762                    &[
763                        (openhcl_musl_target(CommonArch::X86_64), true),
764                        (openhcl_musl_target(CommonArch::Aarch64), true),
765                    ],
766                )),
767                unit_test_target: Some(("x64-linux-musl", openhcl_musl_target(CommonArch::X86_64))),
768            },
769            ClippyUnitTestJobParams {
770                platform: FlowPlatform::Windows,
771                arch: FlowArch::Aarch64,
772                gh_pool: crate::pipelines_shared::gh_pools::windows_arm_self_hosted_baremetal(),
773                clippy_targets: None,
774                unit_test_target: Some((
775                    "aarch64-windows",
776                    target_lexicon::triple!("aarch64-pc-windows-msvc"),
777                )),
778            },
779        ] {
780            let mut job_name = Vec::new();
781            if let Some((label, _)) = &clippy_targets {
782                job_name.push(format!("clippy [{label}]"));
783            }
784            if let Some((label, _)) = &unit_test_target {
785                job_name.push(format!("unit tests [{label}]"));
786            }
787            let job_name = job_name.join(", ");
788
789            let unit_test_target = unit_test_target.map(|(label, target)| {
790                let test_label = format!("{label}-unit-tests");
791                let pub_unit_test_junit_xml = if matches!(backend_hint, PipelineBackendHint::Local)
792                {
793                    Some(pipeline.new_artifact(&test_label).0)
794                } else {
795                    None
796                };
797                (test_label, target, pub_unit_test_junit_xml)
798            });
799
800            let mut clippy_unit_test_job = pipeline
801                .new_job(platform, arch, job_name)
802                .gh_set_pool(gh_pool);
803
804            if let Some((_, targets)) = clippy_targets {
805                for (target, also_check_misc_nostd_crates) in targets {
806                    clippy_unit_test_job = clippy_unit_test_job.dep_on(|ctx| {
807                        flowey_lib_hvlite::_jobs::check_clippy::Request {
808                            target: target.clone(),
809                            profile: CommonProfile::from_release(release),
810                            done: ctx.new_done_handle(),
811                            also_check_misc_nostd_crates: *also_check_misc_nostd_crates,
812                        }
813                    });
814                }
815            }
816
817            if let Some((test_label, target, pub_unit_test_junit_xml)) = unit_test_target {
818                clippy_unit_test_job = clippy_unit_test_job
819                    .dep_on(|ctx| {
820                        flowey_lib_hvlite::_jobs::build_and_run_nextest_unit_tests::Params {
821                            junit_test_label: test_label,
822                            nextest_profile:
823                                flowey_lib_hvlite::run_cargo_nextest_run::NextestProfile::Ci,
824                            fail_job_on_test_fail: true,
825                            target: target.clone(),
826                            profile: CommonProfile::from_release(release),
827                            unstable_panic_abort_tests: None,
828                            artifact_dir: pub_unit_test_junit_xml.map(|x| ctx.publish_artifact(x)),
829                            done: ctx.new_done_handle(),
830                        }
831                    })
832                    .dep_on(
833                        |ctx| flowey_lib_hvlite::_jobs::build_and_run_doc_tests::Params {
834                            target,
835                            profile: CommonProfile::from_release(release),
836                            done: ctx.new_done_handle(),
837                        },
838                    );
839            }
840
841            all_jobs.push(clippy_unit_test_job.finish());
842        }
843
844        let vmm_tests_artifacts_windows_intel_x86 = vmm_tests_artifacts_windows_x86
845            .clone()
846            .finish()
847            .map_err(|missing| {
848                anyhow::anyhow!("missing required windows-intel vmm_tests artifact: {missing}")
849            })?;
850        let vmm_tests_artifacts_windows_intel_tdx_x86 = vmm_tests_artifacts_windows_x86
851            .clone()
852            .finish()
853            .map_err(|missing| {
854                anyhow::anyhow!("missing required windows-intel-tdx vmm_tests artifact: {missing}")
855            })?;
856        let vmm_tests_artifacts_windows_amd_x86 = vmm_tests_artifacts_windows_x86
857            .clone()
858            .finish()
859            .map_err(|missing| {
860                anyhow::anyhow!("missing required windows-amd vmm_tests artifact: {missing}")
861            })?;
862        let vmm_tests_artifacts_windows_amd_snp_x86 = vmm_tests_artifacts_windows_x86
863            .finish()
864            .map_err(|missing| {
865                anyhow::anyhow!("missing required windows-amd-snp vmm_tests artifact: {missing}")
866            })?;
867        let vmm_tests_artifacts_linux_x86 =
868            vmm_tests_artifacts_linux_x86.finish().map_err(|missing| {
869                anyhow::anyhow!("missing required linux vmm_tests artifact: {missing}")
870            })?;
871        let vmm_tests_artifacts_windows_aarch64 = vmm_tests_artifacts_windows_aarch64
872            .finish()
873            .map_err(|missing| {
874                anyhow::anyhow!("missing required windows-aarch64 vmm_tests artifact: {missing}")
875            })?;
876
877        // Emit VMM tests runner jobs
878        struct VmmTestJobParams<'a> {
879            platform: FlowPlatform,
880            arch: FlowArch,
881            gh_pool: GhRunner,
882            label: &'a str,
883            target: CommonTriple,
884            resolve_vmm_tests_artifacts: vmm_tests_artifact_builders::ResolveVmmTestsDepArtifacts,
885            nextest_filter_expr: String,
886            test_artifacts: Vec<KnownTestArtifacts>,
887        }
888
889        // standard VM-based CI machines should be able to run all tests except
890        // those that require special hardware features (tdx/snp) or need to be
891        // run on a baremetal host (hyper-v vbs doesn't seem to work nested)
892        let standard_filter =
893            "all() & !test(tdx) & !test(snp) & !(test(vbs) & test(hyperv))".to_string();
894        let standard_x64_test_artifacts = vec![
895            KnownTestArtifacts::FreeBsd13_2X64Vhd,
896            KnownTestArtifacts::FreeBsd13_2X64Iso,
897            KnownTestArtifacts::Gen1WindowsDataCenterCore2022X64Vhd,
898            KnownTestArtifacts::Gen2WindowsDataCenterCore2022X64Vhd,
899            KnownTestArtifacts::Ubuntu2204ServerX64Vhd,
900            KnownTestArtifacts::VmgsWithBootEntry,
901        ];
902
903        let cvm_filter = |arch| format!("test({arch}) + (test(vbs) & test(hyperv))");
904        let cvm_x64_test_artifacts = vec![
905            KnownTestArtifacts::Gen2WindowsDataCenterCore2025X64Vhd,
906            KnownTestArtifacts::Ubuntu2404ServerX64Vhd,
907        ];
908
909        for VmmTestJobParams {
910            platform,
911            arch,
912            gh_pool,
913            label,
914            target,
915            resolve_vmm_tests_artifacts,
916            nextest_filter_expr,
917            test_artifacts,
918        } in [
919            VmmTestJobParams {
920                platform: FlowPlatform::Windows,
921                arch: FlowArch::X86_64,
922                gh_pool: crate::pipelines_shared::gh_pools::windows_intel_self_hosted_largedisk(),
923                label: "x64-windows-intel",
924                target: CommonTriple::X86_64_WINDOWS_MSVC,
925                resolve_vmm_tests_artifacts: vmm_tests_artifacts_windows_intel_x86,
926                nextest_filter_expr: standard_filter.clone(),
927                test_artifacts: standard_x64_test_artifacts.clone(),
928            },
929            VmmTestJobParams {
930                platform: FlowPlatform::Windows,
931                arch: FlowArch::X86_64,
932                gh_pool: crate::pipelines_shared::gh_pools::windows_tdx_self_hosted_baremetal(),
933                label: "x64-windows-intel-tdx",
934                target: CommonTriple::X86_64_WINDOWS_MSVC,
935                resolve_vmm_tests_artifacts: vmm_tests_artifacts_windows_intel_tdx_x86,
936                nextest_filter_expr: cvm_filter("tdx"),
937                test_artifacts: cvm_x64_test_artifacts.clone(),
938            },
939            VmmTestJobParams {
940                platform: FlowPlatform::Windows,
941                arch: FlowArch::X86_64,
942                gh_pool: crate::pipelines_shared::gh_pools::windows_amd_self_hosted_largedisk(),
943                label: "x64-windows-amd",
944                target: CommonTriple::X86_64_WINDOWS_MSVC,
945                resolve_vmm_tests_artifacts: vmm_tests_artifacts_windows_amd_x86,
946                nextest_filter_expr: standard_filter.clone(),
947                test_artifacts: standard_x64_test_artifacts.clone(),
948            },
949            VmmTestJobParams {
950                platform: FlowPlatform::Windows,
951                arch: FlowArch::X86_64,
952                gh_pool: crate::pipelines_shared::gh_pools::windows_snp_self_hosted_baremetal(),
953                label: "x64-windows-amd-snp",
954                target: CommonTriple::X86_64_WINDOWS_MSVC,
955                resolve_vmm_tests_artifacts: vmm_tests_artifacts_windows_amd_snp_x86,
956                nextest_filter_expr: cvm_filter("snp"),
957                test_artifacts: cvm_x64_test_artifacts,
958            },
959            VmmTestJobParams {
960                platform: FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
961                arch: FlowArch::X86_64,
962                gh_pool: crate::pipelines_shared::gh_pools::linux_self_hosted(),
963                label: "x64-linux",
964                target: CommonTriple::X86_64_LINUX_GNU,
965                resolve_vmm_tests_artifacts: vmm_tests_artifacts_linux_x86,
966                // - No legal way to obtain gen1 pcat blobs on non-msft linux machines
967                nextest_filter_expr: format!("{standard_filter} & !test(pcat_x64)"),
968                test_artifacts: standard_x64_test_artifacts,
969            },
970            VmmTestJobParams {
971                platform: FlowPlatform::Windows,
972                arch: FlowArch::Aarch64,
973                gh_pool: crate::pipelines_shared::gh_pools::windows_arm_self_hosted_baremetal(),
974                label: "aarch64-windows",
975                target: CommonTriple::AARCH64_WINDOWS_MSVC,
976                resolve_vmm_tests_artifacts: vmm_tests_artifacts_windows_aarch64,
977                nextest_filter_expr: "all()".to_string(),
978                test_artifacts: vec![
979                    KnownTestArtifacts::Ubuntu2404ServerAarch64Vhd,
980                    KnownTestArtifacts::Windows11EnterpriseAarch64Vhdx,
981                    KnownTestArtifacts::VmgsWithBootEntry,
982                ],
983            },
984        ] {
985            let test_label = format!("{label}-vmm-tests");
986
987            let pub_vmm_tests_results = if matches!(backend_hint, PipelineBackendHint::Local) {
988                Some(pipeline.new_artifact(&test_label).0)
989            } else {
990                None
991            };
992
993            let use_vmm_tests_archive = match target {
994                CommonTriple::X86_64_WINDOWS_MSVC => &use_vmm_tests_archive_windows_x86,
995                CommonTriple::X86_64_LINUX_GNU => &use_vmm_tests_archive_linux_x86,
996                CommonTriple::AARCH64_WINDOWS_MSVC => &use_vmm_tests_archive_windows_aarch64,
997                _ => unreachable!(),
998            };
999
1000            let mut vmm_tests_run_job = pipeline
1001                .new_job(platform, arch, format!("run vmm-tests [{label}]"))
1002                .gh_set_pool(gh_pool)
1003                .dep_on(|ctx| {
1004                    flowey_lib_hvlite::_jobs::consume_and_test_nextest_vmm_tests_archive::Params {
1005                        junit_test_label: test_label,
1006                        nextest_vmm_tests_archive: ctx.use_typed_artifact(use_vmm_tests_archive),
1007                        target: target.as_triple(),
1008                        nextest_profile:
1009                            flowey_lib_hvlite::run_cargo_nextest_run::NextestProfile::Ci,
1010                        nextest_filter_expr: Some(nextest_filter_expr),
1011                        dep_artifact_dirs: resolve_vmm_tests_artifacts(ctx),
1012                        test_artifacts,
1013                        fail_job_on_test_fail: true,
1014                        artifact_dir: pub_vmm_tests_results.map(|x| ctx.publish_artifact(x)),
1015                        done: ctx.new_done_handle(),
1016                    }
1017                });
1018
1019            if let Some(vmm_tests_disk_cache_dir) = vmm_tests_disk_cache_dir.clone() {
1020                vmm_tests_run_job = vmm_tests_run_job.dep_on(|_| {
1021                    flowey_lib_hvlite::download_openvmm_vmm_tests_artifacts::Request::CustomCacheDir(
1022                        vmm_tests_disk_cache_dir,
1023                    )
1024                })
1025            }
1026
1027            all_jobs.push(vmm_tests_run_job.finish());
1028        }
1029
1030        // test the flowey local backend by running cargo xflowey build-igvm on x64
1031        {
1032            let job = pipeline
1033                .new_job(
1034                    FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
1035                    FlowArch::X86_64,
1036                    "test flowey local backend",
1037                )
1038                .gh_set_pool(crate::pipelines_shared::gh_pools::default_x86_pool(
1039                    FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
1040                ))
1041                .dep_on(
1042                    |ctx| flowey_lib_hvlite::_jobs::test_local_flowey_build_igvm::Request {
1043                        base_recipe: OpenhclIgvmRecipe::X64,
1044                        done: ctx.new_done_handle(),
1045                    },
1046                )
1047                .finish();
1048            all_jobs.push(job);
1049        }
1050
1051        if matches!(config, PipelineConfig::Pr) {
1052            // Add a job that depends on all others as a workaround for
1053            // https://github.com/orgs/community/discussions/12395.
1054            //
1055            // This workaround then itself requires _another_ workaround, requiring
1056            // the use of `gh_dangerous_override_if`, and some additional custom job
1057            // logic, to deal with https://github.com/actions/runner/issues/2566.
1058            //
1059            // TODO: Add a way for this job to skip flowey setup and become a true
1060            // no-op.
1061            let all_good_job = pipeline
1062                .new_job(
1063                    FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
1064                    FlowArch::X86_64,
1065                    "openvmm checkin gates",
1066                )
1067                .gh_set_pool(crate::pipelines_shared::gh_pools::default_gh_hosted(
1068                    FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
1069                ))
1070                // always run this job, regardless whether or not any previous jobs failed
1071                .gh_dangerous_override_if("always() && github.event.pull_request.draft == false")
1072                .gh_dangerous_global_env_var("ANY_JOBS_FAILED", "${{ contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'failure') }}")
1073                .dep_on(|ctx| flowey_lib_hvlite::_jobs::all_good_job::Params {
1074                    did_fail_env_var: "ANY_JOBS_FAILED".into(),
1075                    done: ctx.new_done_handle(),
1076                })
1077                .finish();
1078
1079            for job in all_jobs.iter() {
1080                pipeline.non_artifact_dep(&all_good_job, job);
1081            }
1082        }
1083
1084        Ok(pipeline)
1085    }
1086}
1087
1088/// Utility builders which make it easy to "skim off" artifacts required by VMM
1089/// test execution from other pipeline jobs.
1090//
1091// FUTURE: if we end up having a _lot_ of VMM test jobs, this would be the sort
1092// of thing that would really benefit from a derive macro.
1093mod vmm_tests_artifact_builders {
1094    use flowey::pipeline::prelude::*;
1095    use flowey_lib_hvlite::_jobs::consume_and_test_nextest_vmm_tests_archive::VmmTestsDepArtifacts;
1096    use flowey_lib_hvlite::build_guest_test_uefi::GuestTestUefiOutput;
1097    use flowey_lib_hvlite::build_openvmm::OpenvmmOutput;
1098    use flowey_lib_hvlite::build_pipette::PipetteOutput;
1099    use flowey_lib_hvlite::build_tmk_vmm::TmkVmmOutput;
1100    use flowey_lib_hvlite::build_tmks::TmksOutput;
1101
1102    pub type ResolveVmmTestsDepArtifacts =
1103        Box<dyn Fn(&mut PipelineJobCtx<'_>) -> VmmTestsDepArtifacts>;
1104
1105    #[derive(Default)]
1106    pub struct VmmTestsArtifactsBuilderLinuxX86 {
1107        // windows build machine
1108        pub use_pipette_windows: Option<UseTypedArtifact<PipetteOutput>>,
1109        pub use_tmk_vmm: Option<UseTypedArtifact<TmkVmmOutput>>,
1110        // linux build machine
1111        pub use_openvmm: Option<UseTypedArtifact<OpenvmmOutput>>,
1112        pub use_pipette_linux_musl: Option<UseTypedArtifact<PipetteOutput>>,
1113        // any machine
1114        pub use_guest_test_uefi: Option<UseTypedArtifact<GuestTestUefiOutput>>,
1115        pub use_tmks: Option<UseTypedArtifact<TmksOutput>>,
1116    }
1117
1118    impl VmmTestsArtifactsBuilderLinuxX86 {
1119        pub fn finish(self) -> Result<ResolveVmmTestsDepArtifacts, &'static str> {
1120            let VmmTestsArtifactsBuilderLinuxX86 {
1121                use_openvmm,
1122                use_guest_test_uefi,
1123                use_pipette_windows,
1124                use_pipette_linux_musl,
1125                use_tmk_vmm,
1126                use_tmks,
1127            } = self;
1128
1129            let use_guest_test_uefi = use_guest_test_uefi.ok_or("guest_test_uefi")?;
1130            let use_openvmm = use_openvmm.ok_or("openvmm")?;
1131            let use_pipette_linux_musl = use_pipette_linux_musl.ok_or("pipette_linux_musl")?;
1132            let use_pipette_windows = use_pipette_windows.ok_or("pipette_windows")?;
1133            let use_tmk_vmm = use_tmk_vmm.ok_or("tmk_vmm")?;
1134            let use_tmks = use_tmks.ok_or("tmks")?;
1135
1136            Ok(Box::new(move |ctx| VmmTestsDepArtifacts {
1137                openvmm: Some(ctx.use_typed_artifact(&use_openvmm)),
1138                pipette_windows: Some(ctx.use_typed_artifact(&use_pipette_windows)),
1139                pipette_linux_musl: Some(ctx.use_typed_artifact(&use_pipette_linux_musl)),
1140                guest_test_uefi: Some(ctx.use_typed_artifact(&use_guest_test_uefi)),
1141                tmk_vmm: Some(ctx.use_typed_artifact(&use_tmk_vmm)),
1142                tmks: Some(ctx.use_typed_artifact(&use_tmks)),
1143                // not currently required, since OpenHCL tests cannot be run on OpenVMM on linux
1144                artifact_dir_openhcl_igvm_files: None,
1145                tmk_vmm_linux_musl: None,
1146            }))
1147        }
1148    }
1149
1150    #[derive(Default, Clone)]
1151    pub struct VmmTestsArtifactsBuilderWindowsX86 {
1152        // windows build machine
1153        pub use_openvmm: Option<UseTypedArtifact<OpenvmmOutput>>,
1154        pub use_pipette_windows: Option<UseTypedArtifact<PipetteOutput>>,
1155        pub use_tmk_vmm: Option<UseTypedArtifact<TmkVmmOutput>>,
1156        // linux build machine
1157        pub use_openhcl_igvm_files: Option<UseArtifact>,
1158        pub use_pipette_linux_musl: Option<UseTypedArtifact<PipetteOutput>>,
1159        pub use_tmk_vmm_linux_musl: Option<UseTypedArtifact<TmkVmmOutput>>,
1160        // any machine
1161        pub use_guest_test_uefi: Option<UseTypedArtifact<GuestTestUefiOutput>>,
1162        pub use_tmks: Option<UseTypedArtifact<TmksOutput>>,
1163    }
1164
1165    impl VmmTestsArtifactsBuilderWindowsX86 {
1166        pub fn finish(self) -> Result<ResolveVmmTestsDepArtifacts, &'static str> {
1167            let VmmTestsArtifactsBuilderWindowsX86 {
1168                use_openvmm,
1169                use_pipette_windows,
1170                use_pipette_linux_musl,
1171                use_guest_test_uefi,
1172                use_openhcl_igvm_files,
1173                use_tmk_vmm,
1174                use_tmk_vmm_linux_musl,
1175                use_tmks,
1176            } = self;
1177
1178            let use_openvmm = use_openvmm.ok_or("openvmm")?;
1179            let use_pipette_windows = use_pipette_windows.ok_or("pipette_windows")?;
1180            let use_pipette_linux_musl = use_pipette_linux_musl.ok_or("pipette_linux_musl")?;
1181            let use_guest_test_uefi = use_guest_test_uefi.ok_or("guest_test_uefi")?;
1182            let use_openhcl_igvm_files = use_openhcl_igvm_files.ok_or("openhcl_igvm_files")?;
1183            let use_tmk_vmm = use_tmk_vmm.ok_or("tmk_vmm")?;
1184            let use_tmk_vmm_linux_musl = use_tmk_vmm_linux_musl.ok_or("tmk_vmm_linux_musl")?;
1185            let use_tmks = use_tmks.ok_or("tmks")?;
1186
1187            Ok(Box::new(move |ctx| VmmTestsDepArtifacts {
1188                openvmm: Some(ctx.use_typed_artifact(&use_openvmm)),
1189                pipette_windows: Some(ctx.use_typed_artifact(&use_pipette_windows)),
1190                pipette_linux_musl: Some(ctx.use_typed_artifact(&use_pipette_linux_musl)),
1191                guest_test_uefi: Some(ctx.use_typed_artifact(&use_guest_test_uefi)),
1192                artifact_dir_openhcl_igvm_files: Some(ctx.use_artifact(&use_openhcl_igvm_files)),
1193                tmk_vmm: Some(ctx.use_typed_artifact(&use_tmk_vmm)),
1194                tmk_vmm_linux_musl: Some(ctx.use_typed_artifact(&use_tmk_vmm_linux_musl)),
1195                tmks: Some(ctx.use_typed_artifact(&use_tmks)),
1196            }))
1197        }
1198    }
1199
1200    #[derive(Default, Clone)]
1201    pub struct VmmTestsArtifactsBuilderWindowsAarch64 {
1202        // windows build machine
1203        pub use_openvmm: Option<UseTypedArtifact<OpenvmmOutput>>,
1204        pub use_pipette_windows: Option<UseTypedArtifact<PipetteOutput>>,
1205        pub use_tmk_vmm: Option<UseTypedArtifact<TmkVmmOutput>>,
1206        // linux build machine
1207        pub use_openhcl_igvm_files: Option<UseArtifact>,
1208        pub use_pipette_linux_musl: Option<UseTypedArtifact<PipetteOutput>>,
1209        pub use_tmk_vmm_linux_musl: Option<UseTypedArtifact<TmkVmmOutput>>,
1210        // any machine
1211        pub use_guest_test_uefi: Option<UseTypedArtifact<GuestTestUefiOutput>>,
1212        pub use_tmks: Option<UseTypedArtifact<TmksOutput>>,
1213    }
1214
1215    impl VmmTestsArtifactsBuilderWindowsAarch64 {
1216        pub fn finish(self) -> Result<ResolveVmmTestsDepArtifacts, &'static str> {
1217            let VmmTestsArtifactsBuilderWindowsAarch64 {
1218                use_openvmm,
1219                use_pipette_windows,
1220                use_pipette_linux_musl,
1221                use_guest_test_uefi,
1222                use_openhcl_igvm_files,
1223                use_tmk_vmm,
1224                use_tmk_vmm_linux_musl,
1225                use_tmks,
1226            } = self;
1227
1228            let use_openvmm = use_openvmm.ok_or("openvmm")?;
1229            let use_pipette_windows = use_pipette_windows.ok_or("pipette_windows")?;
1230            let use_pipette_linux_musl = use_pipette_linux_musl.ok_or("pipette_linux_musl")?;
1231            let use_guest_test_uefi = use_guest_test_uefi.ok_or("guest_test_uefi")?;
1232            let use_openhcl_igvm_files = use_openhcl_igvm_files.ok_or("openhcl_igvm_files")?;
1233            let use_tmk_vmm = use_tmk_vmm.ok_or("tmk_vmm")?;
1234            let use_tmk_vmm_linux_musl = use_tmk_vmm_linux_musl.ok_or("tmk_vmm_linux_musl")?;
1235            let use_tmks = use_tmks.ok_or("tmks")?;
1236
1237            Ok(Box::new(move |ctx| VmmTestsDepArtifacts {
1238                openvmm: Some(ctx.use_typed_artifact(&use_openvmm)),
1239                pipette_windows: Some(ctx.use_typed_artifact(&use_pipette_windows)),
1240                pipette_linux_musl: Some(ctx.use_typed_artifact(&use_pipette_linux_musl)),
1241                guest_test_uefi: Some(ctx.use_typed_artifact(&use_guest_test_uefi)),
1242                artifact_dir_openhcl_igvm_files: Some(ctx.use_artifact(&use_openhcl_igvm_files)),
1243                tmk_vmm: Some(ctx.use_typed_artifact(&use_tmk_vmm)),
1244                tmk_vmm_linux_musl: Some(ctx.use_typed_artifact(&use_tmk_vmm_linux_musl)),
1245                tmks: Some(ctx.use_typed_artifact(&use_tmks)),
1246            }))
1247        }
1248    }
1249}