-
Notifications
You must be signed in to change notification settings - Fork 98
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ci: add job to verify binary size #475
base: main
Are you sure you want to change the base?
Changes from 45 commits
d5e1b23
3884a0f
99e745b
207280e
40aa921
f7ffdcb
9853a16
e9854fa
12c39aa
7fe1ab7
17ac9d5
9b47097
e9108a5
51cb2a3
bc7963e
a0f9c0d
d7c38a5
77167e0
40363c4
4f01015
e17187d
ce02bdc
1538c7c
b9c3536
ecb554a
b4811dc
e4df6d7
b6bed7a
eea8ab6
c6881b3
584f248
8a01cfb
4ac364d
2d1bf6e
46968a8
3d00b0d
0711d49
2fc057a
1a6c341
2acb5be
b1f5fa6
5a537f2
91e390a
ad99ffc
ffbcee7
ef5977c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9089,6 +9089,7 @@ dependencies = [ | |
"ignore", | ||
"log", | ||
"mbrman", | ||
"object", | ||
"rayon", | ||
"serde", | ||
"serde_json", | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -629,9 +629,9 @@ impl IntoPipeline for CheckinGatesCli { | |
OpenvmmHclBuildProfile::Debug | ||
}; | ||
|
||
let (pub_openhcl_igvm, use_openhcl_igvm) = | ||
let (pub_openhcl_igvm_dev, use_openhcl_igvm_dev) = | ||
pipeline.new_artifact(format!("{arch_tag}-openhcl-igvm")); | ||
let (pub_openhcl_igvm_extras, _use_openhcl_igvm_extras) = | ||
let (pub_openhcl_igvm_extras_dev, _use_openhcl_igvm_extras_dev) = | ||
pipeline.new_artifact(format!("{arch_tag}-openhcl-igvm-extras")); | ||
// also build pipette musl on this job, as until we land the | ||
// refactor that allows building musl without the full openhcl | ||
|
@@ -644,15 +644,15 @@ impl IntoPipeline for CheckinGatesCli { | |
match arch { | ||
CommonArch::X86_64 => { | ||
vmm_tests_artifacts_windows_x86.use_openhcl_igvm_files = | ||
Some(use_openhcl_igvm.clone()); | ||
Some(use_openhcl_igvm_dev.clone()); | ||
vmm_tests_artifacts_windows_x86.use_pipette_linux_musl = | ||
Some(use_pipette_linux_musl.clone()); | ||
vmm_tests_artifacts_linux_x86.use_pipette_linux_musl = | ||
Some(use_pipette_linux_musl.clone()); | ||
} | ||
CommonArch::Aarch64 => { | ||
vmm_tests_artifacts_windows_aarch64.use_openhcl_igvm_files = | ||
Some(use_openhcl_igvm.clone()); | ||
Some(use_openhcl_igvm_dev.clone()); | ||
vmm_tests_artifacts_windows_aarch64.use_pipette_linux_musl = | ||
Some(use_pipette_linux_musl.clone()); | ||
} | ||
|
@@ -685,6 +685,7 @@ impl IntoPipeline for CheckinGatesCli { | |
.dep_on(|ctx| { | ||
flowey_lib_hvlite::_jobs::build_and_publish_openhcl_igvm_from_recipe::Params { | ||
igvm_files: igvm_recipes | ||
.clone() | ||
.into_iter() | ||
.map(|recipe| OpenhclIgvmBuildParams { | ||
profile: openvmm_hcl_profile, | ||
|
@@ -694,9 +695,9 @@ impl IntoPipeline for CheckinGatesCli { | |
))), | ||
}) | ||
.collect(), | ||
artifact_dir_openhcl_igvm: ctx.publish_artifact(pub_openhcl_igvm), | ||
artifact_dir_openhcl_igvm: ctx.publish_artifact(pub_openhcl_igvm_dev), | ||
artifact_dir_openhcl_igvm_extras: ctx | ||
.publish_artifact(pub_openhcl_igvm_extras), | ||
.publish_artifact(pub_openhcl_igvm_extras_dev), | ||
done: ctx.new_done_handle(), | ||
} | ||
}) | ||
|
@@ -713,6 +714,67 @@ impl IntoPipeline for CheckinGatesCli { | |
); | ||
|
||
all_jobs.push(job.finish()); | ||
|
||
if arch == CommonArch::X86_64 && !release { | ||
let (pub_openhcl_igvm_release, _use_openhcl_igvm_release) = | ||
pipeline.new_artifact(format!("{arch_tag}-openhcl-igvm-release")); | ||
let (pub_openhcl_igvm_extras_release, use_openhcl_igvm_extras_release) = | ||
pipeline.new_artifact(format!("{arch_tag}-openhcl-igvm-extras-release")); | ||
|
||
// Do an underhill-ship build for binary size comparison | ||
let job = pipeline | ||
.new_job( | ||
FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu), | ||
FlowArch::X86_64, | ||
format!("build openhcl [{arch_tag}-linux] release"), | ||
) | ||
.gh_set_pool(crate::pipelines_shared::gh_pools::default_x86_pool( | ||
FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu), | ||
)) | ||
.dep_on(|ctx| { | ||
flowey_lib_hvlite::_jobs::build_and_publish_openhcl_igvm_from_recipe::Params { | ||
igvm_files: igvm_recipes | ||
.into_iter() | ||
.map(|recipe| OpenhclIgvmBuildParams { | ||
profile: OpenvmmHclBuildProfile::OpenvmmHclShip, | ||
recipe, | ||
custom_target: Some(CommonTriple::Custom(openhcl_musl_target( | ||
arch, | ||
))), | ||
}) | ||
.collect(), | ||
artifact_dir_openhcl_igvm: ctx.publish_artifact(pub_openhcl_igvm_release), | ||
artifact_dir_openhcl_igvm_extras: ctx | ||
.publish_artifact(pub_openhcl_igvm_extras_release), | ||
done: ctx.new_done_handle(), | ||
} | ||
}); | ||
|
||
all_jobs.push(job.finish()); | ||
|
||
// emit openvmm verify-size job | ||
let job = pipeline | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not use a single job? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indeed. If you actually look inside If you just peel back a layer, and have your new Plus - you wouldn't be shackled to the existing |
||
.new_job( | ||
FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu), | ||
FlowArch::X86_64, | ||
format!("verify openhcl binary size [{}]", arch_tag), | ||
) | ||
.gh_set_pool(crate::pipelines_shared::gh_pools::default_x86_pool( | ||
FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu), | ||
)) | ||
.dep_on( | ||
|ctx| flowey_lib_hvlite::_jobs::check_openvmm_hcl_size::Request { | ||
target: CommonTriple::Common { | ||
arch, | ||
platform: CommonPlatform::LinuxMusl, | ||
}, | ||
new_openhcl: ctx.use_artifact(&use_openhcl_igvm_extras_release), | ||
done: ctx.new_done_handle(), | ||
}, | ||
) | ||
.finish(); | ||
all_jobs.push(job); | ||
} | ||
} | ||
|
||
// Emit clippy + unit-test jobs | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
//! Download a github release artifact | ||
|
||
use flowey::node::prelude::*; | ||
|
||
flowey_request! { | ||
pub struct Request { | ||
/// First component of a github repo path | ||
/// | ||
/// e.g: the "foo" in "github.com/foo/bar" | ||
pub repo_owner: String, | ||
/// Second component of a github repo path | ||
/// | ||
/// e.g: the "bar" in "github.com/foo/bar" | ||
pub repo_name: String, | ||
/// Specific artifact to download. | ||
pub file_name: String, | ||
/// Path to downloaded artifact. | ||
pub path: WriteVar<PathBuf>, | ||
/// The Github actions run id to download artifacts from | ||
pub run_id: ReadVar<String> | ||
} | ||
} | ||
|
||
new_simple_flow_node!(struct Node); | ||
|
||
impl SimpleFlowNode for Node { | ||
type Request = Request; | ||
|
||
fn imports(ctx: &mut ImportCtx<'_>) { | ||
ctx.import::<crate::cache::Node>(); | ||
ctx.import::<crate::use_gh_cli::Node>(); | ||
} | ||
|
||
fn process_request(request: Self::Request, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> { | ||
let Request { | ||
repo_owner, | ||
repo_name, | ||
file_name, | ||
path, | ||
run_id, | ||
} = request; | ||
|
||
let gh_token = ctx.get_gh_context_var(GhContextVar::GITHUB__TOKEN); | ||
ctx.req(crate::use_gh_cli::Request::WithAuth( | ||
crate::use_gh_cli::GhCliAuth::AuthToken(gh_token), | ||
)); | ||
let gh_cli = ctx.reqv(crate::use_gh_cli::Request::Get); | ||
|
||
ctx.emit_rust_step("download artifacts from github actions run", |ctx| { | ||
let gh_cli = gh_cli.claim(ctx); | ||
let run_id = run_id.claim(ctx); | ||
let path = path.claim(ctx); | ||
move |rt| { | ||
let sh = xshell::Shell::new()?; | ||
let gh_cli = rt.read(gh_cli); | ||
let run_id = rt.read(run_id); | ||
|
||
let path_end = format!("{repo_owner}/{repo_name}/{run_id}"); | ||
let out_dir = std::env::current_dir()?.absolute()?.join(path_end); | ||
fs_err::create_dir_all(&out_dir)?; | ||
sh.change_dir(&out_dir); | ||
|
||
xshell::cmd!(sh, "{gh_cli} run download {run_id} -R {repo_owner}/{repo_name} --pattern {file_name}").run()?; | ||
|
||
rt.write(path, &out_dir); | ||
Ok(()) | ||
} | ||
}); | ||
|
||
Ok(()) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. don't forget your module docs There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (here, and elsewhere) |
||
use flowey::node::prelude::*; | ||
|
||
flowey_request! { | ||
pub struct Request { | ||
pub repo_path: ReadVar<PathBuf>, | ||
pub merge_commit: WriteVar<String>, | ||
} | ||
} | ||
|
||
new_simple_flow_node!(struct Node); | ||
|
||
impl SimpleFlowNode for Node { | ||
type Request = Request; | ||
|
||
fn imports(_ctx: &mut ImportCtx<'_>) {} | ||
|
||
fn process_request(request: Self::Request, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> { | ||
let Request { | ||
repo_path, | ||
merge_commit, | ||
} = request; | ||
|
||
let head_ref = ctx.get_gh_context_var(GhContextVar::GITHUB__HEAD_REF); | ||
let pr_number = ctx.get_gh_context_var(GhContextVar::GITHUB__PR_NUMBER); | ||
|
||
ctx.emit_rust_step("get merge commit", |ctx| { | ||
let merge_commit = merge_commit.claim(ctx); | ||
let head_ref = head_ref.claim(ctx); | ||
let repo_path = repo_path.claim(ctx); | ||
let pr_number = pr_number.claim(ctx); | ||
|
||
|rt| { | ||
let sh = xshell::Shell::new()?; | ||
let repo_path = rt.read(repo_path); | ||
let head_ref = rt.read(head_ref); | ||
let pr_number = rt.read(pr_number); | ||
|
||
sh.change_dir(repo_path); | ||
|
||
// TODO: Make this work for non-main PRs | ||
xshell::cmd!(sh, "git fetch origin main").run()?; | ||
xshell::cmd!(sh, "git fetch origin pull/{pr_number}/head:{head_ref}").run()?; | ||
let commit = xshell::cmd!(sh, "git merge-base {head_ref} origin/main").read()?; | ||
rt.write(merge_commit, &commit); | ||
|
||
Ok(()) | ||
} | ||
}); | ||
|
||
Ok(()) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
use flowey::node::prelude::*; | ||
|
||
flowey_request! { | ||
pub struct Request { | ||
pub github_commit_hash: ReadVar<String>, | ||
pub repo_path: ReadVar<PathBuf>, | ||
pub gh_workflow_id: WriteVar<String>, | ||
} | ||
} | ||
|
||
new_simple_flow_node!(struct Node); | ||
|
||
impl SimpleFlowNode for Node { | ||
type Request = Request; | ||
|
||
fn imports(_ctx: &mut ImportCtx<'_>) {} | ||
|
||
fn process_request(request: Self::Request, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> { | ||
let Request { | ||
repo_path, | ||
github_commit_hash, | ||
gh_workflow_id, | ||
} = request; | ||
|
||
let gh_token = ctx.get_gh_context_var(GhContextVar::GITHUB__TOKEN); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmmm, maybe not strictly required for this PR, but do we need some way API like |
||
|
||
ctx.emit_rust_step("get action id", |ctx| { | ||
let gh_workflow_id = gh_workflow_id.claim(ctx); | ||
let github_commit_hash = github_commit_hash.claim(ctx); | ||
let gh_token = gh_token.claim(ctx); | ||
let repo_path = repo_path.claim(ctx); | ||
|
||
|rt| { | ||
let github_commit_hash = rt.read(github_commit_hash); | ||
let sh = xshell::Shell::new()?; | ||
let gh_token = rt.read(gh_token); | ||
let repo_path = rt.read(repo_path); | ||
|
||
sh.change_dir(repo_path); | ||
// Fetches the CI build workflow id for a given commit hash | ||
let get_action_id = |commit: String| { | ||
xshell::cmd!( | ||
sh, | ||
"gh run list --commit {commit} -w '[flowey] OpenVMM CI' -s 'completed' -L 1 --json databaseId --jq '.[].databaseId'" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we def don't want to be hard-coding openvmm specific pipeline names within a |
||
) | ||
.env("GITHUB_TOKEN", gh_token.clone()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this actually necessary? I would expect GitHub Actions to already have this set ambiently? |
||
.read() | ||
}; | ||
|
||
let mut github_commit_hash = github_commit_hash.clone(); | ||
let mut action_id = get_action_id(github_commit_hash.clone()); | ||
let mut loop_count = 0; | ||
|
||
// CI may not have finished the build for the merge base, so loop through commits | ||
// until we find a finished build or fail after 5 attempts | ||
while let Err(ref e) = action_id { | ||
if loop_count > 4 { | ||
anyhow::bail!("Failed to get action id after 5 attempts: {}", e); | ||
} | ||
|
||
github_commit_hash = xshell::cmd!(sh, "git rev-parse {github_commit_hash}^").read()?; | ||
action_id = get_action_id(github_commit_hash.clone()); | ||
loop_count += 1; | ||
} | ||
|
||
if let Ok(id) = action_id { | ||
print!("Got action id: {}", id); | ||
rt.write(gh_workflow_id, &id); | ||
} else { | ||
anyhow::bail!("Failed to get action id"); | ||
} | ||
|
||
Ok(()) | ||
} | ||
}); | ||
|
||
Ok(()) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we want to be mindful about how we choose to expose this particular class of context variables in
flowey
.All other constants defined in this list are guaranteed to be valid in any pipeline run. The same cannot be said of these new
github.event.pull_request
constants, which should only be used in PR-triggered workflows. i.e: it seems unwise to make it "trivial" to access these variables in the context of a CI-triggered workflow via the existingget_gh_context_var
API, given that the resulting loosely-typedString
could sometimes be empty.My gut feeling is that we want to have some API that would let us model these sorts of context-dependent variables in a type-safe manner, in order to give users a way to get a
ReadVar<Option<PullRequestRelatedThing>>
.Consider the following modification to the existing
get_gh_context_var
API:The resulting API is very fluent for end users, and to make our lives easier as implementors, we can leverage the type-state pattern to avoid an explosion of different
GhContextVarReader
types, and instead, simply transition between various versions of a single backingGhContextVarReader<T>
type.With this API, the leaf-nodes (e.g:
pull_request().head().ref()
) can then encode any necessary flowey logic to read the rawString
data, and then convert it into aOption<String>
if need be.I know this is all somewhat orthogonal to the problem this PR is specifically trying to solve... but we must not forget that flowey is not a framework set in stone. It still has many rough edges and core abstractions that need to be reconsidered / reworked. This sort of flowey rework can and should be done as part of whatever feature work we are doing.
In this case, I don't actually think there's too much work to re-jig the API as I suggest here - there shouldn't be any "backend" flowey work, and it'd simply be some clever refactoring of the user-facing
NodeContext
APIs.That said - I would suggest you split this work out into a separate PR, and then rebase this PR on-top of that one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
theoretically, it might even make sense to have a single
pull_request() -> ReadVar<Option<GhEventPullRequest>>
method, which dumps the entiregithub.events.pull_request
variable as JSON, and then flowey uses aserde
defn of the corresponding object to avoid needing to manually write out methods for each field. See https://docs.github.com/en/webhooks/webhook-events-and-payloads#pull_requestIts a "big" object, but its not a huge object, so I think that might be a viable approach - even if the pipeline only ends up using one or two fields of the object that gets parsed in.
And if its hard to transcribe that particular object structure in its entirety into a serde type, we can always go piecemeal (given that serde will just ignore JSON fields during deserialization that it wasn't explicitly told about)