Skip to content

Commit c00037e

Browse files
committed
continue building when a remote build fails and report errors afterwards
1 parent a70a339 commit c00037e

File tree

2 files changed

+102
-22
lines changed

2 files changed

+102
-22
lines changed

src/cli.rs

+100-20
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ use std::collections::HashMap;
77
use std::io::{stdin, stdout, Write};
88

99
use clap::{ArgMatches, Clap, FromArgMatches};
10-
use futures_util::future::{join_all, try_join_all};
11-
use tokio::try_join;
10+
use futures_util::future::join_all;
11+
use tokio::join;
1212

13-
use crate as deploy;
13+
use crate::{self as deploy, CmdOverrides, DeployDefs};
1414
use crate::push::{PushProfileData, PushProfileError};
1515

1616
use self::deploy::{DeployFlake, ParseFlakeError};
@@ -602,27 +602,113 @@ async fn run_deploy(
602602
}
603603
});
604604

605-
try_join!(
605+
async fn deploy_profiles_to_host<'a>((host, profiles): (&str, Vec<&'a PushProfileData<'a>>)) -> Result<(), (String, String, PushProfileError)> {
606+
for profile in &profiles {
607+
deploy::push::build_profile(profile).await.map_err(|e| (host.to_string(), profile.deploy_data.profile_name.to_string(), e))?;
608+
};
609+
Ok(())
610+
}
611+
612+
let (remote_results, local_results) = join!(
613+
// remote builds can be run asynchronously (per host)
614+
join_all(remote_build_map.into_iter().map(deploy_profiles_to_host)),
615+
606616
// remote builds can be run asynchronously (per host)
607-
try_join_all(remote_build_map.into_iter().map(deploy_profiles_to_host)),
608-
async {
617+
async move {
618+
let mut build_results = Vec::new();
619+
let mut push_futures = Vec::new();
620+
609621
// run local builds synchronously to prevent hardware deadlocks
610-
for data in &local_builds {
611-
deploy::push::build_profile(data).await.unwrap();
622+
for data in local_builds {
623+
let res = deploy::push::build_profile(&data).await;
624+
if res.is_ok() {
625+
// unfortunately, all of this is necessary to please the borrow checker
626+
// since deploy_data is a nested struct of references, the spawned process
627+
// could outlive the scope of this function where the references' values are
628+
// defined
629+
let node_name = data.deploy_data.node_name.to_string();
630+
let node = data.deploy_data.node.clone();
631+
let profile_name = data.deploy_data.profile_name.to_string();
632+
let profile = data.deploy_data.profile.clone();
633+
let cmd_overrides = data.deploy_data.cmd_overrides.clone();
634+
let merged_settings = data.deploy_data.merged_settings.clone();
635+
let debug_logs = data.deploy_data.debug_logs.clone();
636+
let log_dir = data.deploy_data.log_dir.map(|x| x.to_string());
637+
let extra_build_args: Vec<String> = data.extra_build_args.iter().cloned().collect();
638+
let result_path = data.result_path.map(|x| x.to_string());
639+
let keep_result = data.keep_result.clone();
640+
let deploy_defs = data.deploy_defs.clone();
641+
let repo = data.repo.to_string();
642+
let check_sigs = data.check_sigs.clone();
643+
644+
let handle = tokio::spawn(async move {
645+
let deploy_data = deploy::DeployData {
646+
node_name: &node_name,
647+
node: &node,
648+
profile_name: &profile_name,
649+
profile: &profile,
650+
cmd_overrides: &cmd_overrides,
651+
merged_settings,
652+
debug_logs,
653+
log_dir: log_dir.as_deref()
654+
};
655+
656+
let data = PushProfileData {
657+
check_sigs,
658+
repo: &repo,
659+
deploy_data: &deploy_data,
660+
deploy_defs: &deploy_defs,
661+
keep_result,
662+
result_path: result_path.as_deref(),
663+
extra_build_args: &extra_build_args,
664+
supports_flakes: true,
665+
};
666+
667+
deploy::push::push_profile(&data).await
668+
});
669+
push_futures.push(handle);
670+
}
671+
build_results.push(res);
612672
}
613673

674+
let push_results = join_all(push_futures).await;
614675

615-
// push all profiles asynchronously
616-
join_all(local_builds.into_iter().map(|data| async {
617-
let data = data;
618-
deploy::push::push_profile(&data).await
619-
})).await;
620-
Ok(())
676+
(build_results, push_results)
621677
}
622678
).map_err(|e| {
623679
RunDeployError::BuildProfile(node_name, e)
624680
})?;
625681

682+
for result in local_results.0 {
683+
match result {
684+
Err(e) => {
685+
error!("failed building profile locally: {:?}", e);
686+
return Err(RunDeployError::PushProfile(e));
687+
},
688+
_ => (),
689+
}
690+
}
691+
692+
for result in local_results.1 {
693+
match result {
694+
Err(e) => panic!("failed to join future: {}, please open a bug report", e),
695+
Ok(Err(e)) => {
696+
error!("failed pushing profile: {:?}", e);
697+
return Err(RunDeployError::PushProfile(e));
698+
},
699+
_ => (),
700+
}
701+
}
702+
703+
for result in remote_results {
704+
match result {
705+
Err((host, profile, e)) => {
706+
error!("failed building profile {} on host {}: {:?}", profile, host, e);
707+
return Err(RunDeployError::PushProfile(e));
708+
},
709+
_ => (),
710+
}
711+
}
626712

627713
let mut succeeded: Vec<(&deploy::DeployData, &deploy::DeployDefs)> = vec![];
628714

@@ -756,9 +842,3 @@ pub async fn run(args: Option<&ArgMatches>) -> Result<(), RunError> {
756842
Ok(())
757843
}
758844

759-
async fn deploy_profiles_to_host<'a>((_host, profiles): (&str, Vec<&'a PushProfileData<'a>>)) -> Result<(), PushProfileError> {
760-
for profile in &profiles {
761-
deploy::push::build_profile(profile).await?;
762-
};
763-
Ok(())
764-
}

src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ pub mod data;
152152
pub mod deploy;
153153
pub mod push;
154154

155-
#[derive(Debug)]
155+
#[derive(Debug, Clone)]
156156
pub struct CmdOverrides {
157157
pub ssh_user: Option<String>,
158158
pub profile_user: Option<String>,
@@ -330,7 +330,7 @@ pub struct DeployData<'a> {
330330
pub log_dir: Option<&'a str>,
331331
}
332332

333-
#[derive(Debug)]
333+
#[derive(Debug, Clone)]
334334
pub struct DeployDefs {
335335
pub ssh_user: String,
336336
pub profile_user: String,

0 commit comments

Comments
 (0)