Skip to content

Commit dd7ec8c

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

File tree

2 files changed

+103
-23
lines changed

2 files changed

+103
-23
lines changed

src/cli.rs

+101-21
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};
@@ -600,25 +600,111 @@ async fn run_deploy(
600600
}
601601
});
602602

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

612-
// push all profiles asynchronously
613-
join_all(local_builds.into_iter().map(|data| async {
614-
let data = data;
615-
deploy::push::push_profile(&data).await
616-
})).await;
672+
let push_results = join_all(push_futures).await;
617673

618-
Ok(())
674+
(build_results, push_results)
619675
}
620-
)?;
676+
);
621677

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

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

@@ -750,9 +836,3 @@ pub async fn run(args: Option<&ArgMatches>) -> Result<(), RunError> {
750836
Ok(())
751837
}
752838

753-
async fn deploy_profiles_to_host<'a>((_host, profiles): (&str, Vec<&'a PushProfileData<'a>>)) -> Result<(), PushProfileError> {
754-
for profile in &profiles {
755-
deploy::push::build_profile(profile).await?;
756-
};
757-
Ok(())
758-
}

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)