flowey_lib_hvlite/_jobs/
check_openvmm_hcl_size.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Compares the size of the OpenHCL binary in the current PR with the size of the binary from the last successful merge to main.
5
6use crate::artifact_openhcl_igvm_from_recipe_extras;
7use crate::build_openhcl_igvm_from_recipe;
8use crate::build_openhcl_igvm_from_recipe::OpenhclIgvmRecipe;
9use crate::build_openvmm_hcl;
10use crate::build_openvmm_hcl::OpenvmmHclBuildParams;
11use crate::build_openvmm_hcl::OpenvmmHclBuildProfile::OpenvmmHclShip;
12use crate::run_cargo_build::common::CommonArch;
13use crate::run_cargo_build::common::CommonTriple;
14use flowey::node::prelude::*;
15use flowey_lib_common::download_gh_artifact;
16use flowey_lib_common::gh_workflow_id;
17use flowey_lib_common::git_merge_commit;
18
19flowey_request! {
20    pub struct Request {
21        pub target: CommonTriple,
22        pub done: WriteVar<SideEffect>,
23        pub pipeline_name: String,
24    }
25}
26
27new_simple_flow_node!(struct Node);
28
29impl SimpleFlowNode for Node {
30    type Request = Request;
31
32    fn imports(ctx: &mut ImportCtx<'_>) {
33        ctx.import::<crate::build_xtask::Node>();
34        ctx.import::<crate::git_checkout_openvmm_repo::Node>();
35        ctx.import::<download_gh_artifact::Node>();
36        ctx.import::<git_merge_commit::Node>();
37        ctx.import::<gh_workflow_id::Node>();
38        ctx.import::<build_openhcl_igvm_from_recipe::Node>();
39        ctx.import::<build_openvmm_hcl::Node>();
40        ctx.import::<artifact_openhcl_igvm_from_recipe_extras::publish::Node>();
41    }
42
43    fn process_request(request: Self::Request, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
44        let Request {
45            target,
46            done,
47            pipeline_name,
48        } = request;
49
50        let xtask = ctx.reqv(|v| crate::build_xtask::Request {
51            target: target.clone(),
52            xtask: v,
53        });
54        let openvmm_repo_path = ctx.reqv(crate::git_checkout_openvmm_repo::req::GetRepoDir);
55
56        let gh_token = ctx.get_gh_context_var().global().token();
57
58        let built_openvmm_hcl = ctx.reqv(|v| build_openvmm_hcl::Request {
59            build_params: OpenvmmHclBuildParams {
60                target: target.clone(),
61                profile: OpenvmmHclShip,
62                features: (OpenhclIgvmRecipe::X64)
63                    .recipe_details(OpenvmmHclShip)
64                    .openvmm_hcl_features,
65                no_split_dbg_info: false,
66            },
67            openvmm_hcl_output: v,
68        });
69
70        let file_name = match target.common_arch().unwrap() {
71            CommonArch::X86_64 => "x64-openhcl-baseline",
72            CommonArch::Aarch64 => "aarch64-openhcl-baseline",
73        };
74
75        let merge_commit = ctx.reqv(|v| git_merge_commit::Request {
76            repo_path: openvmm_repo_path.clone(),
77            merge_commit: v,
78            base_branch: "main".into(),
79        });
80
81        let merge_run = ctx.reqv(|v| gh_workflow_id::Request {
82            repo_path: openvmm_repo_path.clone(),
83            github_commit_hash: merge_commit,
84            gh_workflow: v,
85            pipeline_name,
86            gh_token: gh_token.clone(),
87        });
88
89        let run_id = merge_run.map(ctx, |r| r.id);
90        let merge_head_artifact = ctx.reqv(|old_openhcl| download_gh_artifact::Request {
91            repo_owner: "microsoft".into(),
92            repo_name: "openvmm".into(),
93            file_name: file_name.into(),
94            path: old_openhcl,
95            run_id,
96            gh_token: gh_token.clone(),
97        });
98
99        // Publish the built binary as an artifact for offline analysis.
100        //
101        // FUTURE: Flowey should have a general mechanism for this. We cannot
102        // use the existing artifact support because all artifacts are only
103        // published at the end of the job, if everything else succeeds.
104        let publish_artifact = if ctx.backend() == FlowBackend::Github {
105            let dir = ctx.emit_rust_stepv("collect openvmm_hcl files for analysis", |ctx| {
106                let built_openvmm_hcl = built_openvmm_hcl.clone().claim(ctx);
107                move |rt| {
108                    let built_openvmm_hcl = rt.read(built_openvmm_hcl);
109                    let path = Path::new("artifact");
110                    fs_err::create_dir_all(path)?;
111                    fs_err::copy(built_openvmm_hcl.bin, path.join("openvmm_hcl"))?;
112                    if let Some(dbg) = built_openvmm_hcl.dbg {
113                        fs_err::copy(dbg, path.join("openvmm_hcl.dbg"))?;
114                    }
115                    Ok(path
116                        .absolute()?
117                        .into_os_string()
118                        .into_string()
119                        .ok()
120                        .unwrap())
121                }
122            });
123            let name = format!(
124                "{}_openvmm_hcl_for_size_analysis",
125                target.common_arch().unwrap().as_arch()
126            );
127            Some(
128                ctx.emit_gh_step(
129                    "publish openvmm_hcl for analysis",
130                    "actions/upload-artifact@v4",
131                )
132                .with("name", name)
133                .with("path", dir)
134                .finish(ctx),
135            )
136        } else {
137            None
138        };
139
140        let comparison = ctx.emit_rust_step("binary size comparison", |ctx| {
141            // Ensure the artifact is published before the analysis since this step may fail.
142            let _publish_artifact = publish_artifact.claim(ctx);
143            let xtask = xtask.claim(ctx);
144            let openvmm_repo_path = openvmm_repo_path.claim(ctx);
145            let old_openhcl = merge_head_artifact.claim(ctx);
146            let new_openhcl = built_openvmm_hcl.claim(ctx);
147            let merge_run = merge_run.claim(ctx);
148
149            move |rt| {
150                let xtask = match rt.read(xtask) {
151                    crate::build_xtask::XtaskOutput::LinuxBin { bin, .. } => bin,
152                    crate::build_xtask::XtaskOutput::WindowsBin { exe, .. } => exe,
153                };
154
155                let old_openhcl = rt.read(old_openhcl);
156                let new_openhcl = rt.read(new_openhcl);
157                let merge_run = rt.read(merge_run);
158
159                let old_path = old_openhcl.join(file_name).join("openhcl");
160                let new_path = new_openhcl.bin;
161
162                println!(
163                    "comparing HEAD to merge commit {} and workflow {}",
164                    merge_run.commit, merge_run.id
165                );
166
167                let sh = xshell::Shell::new()?;
168                sh.change_dir(rt.read(openvmm_repo_path));
169                xshell::cmd!(
170                    sh,
171                    "{xtask} verify-size --original {old_path} --new {new_path}"
172                )
173                .run()?;
174
175                Ok(())
176            }
177        });
178
179        ctx.emit_side_effect_step(vec![comparison], [done]);
180
181        Ok(())
182    }
183}