Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 32 additions & 18 deletions crates/icp-cli/src/commands/build/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,8 @@ pub async fn exec(ctx: &Context, args: &BuildArgs) -> Result<(), CommandError> {
for (i, step) in c.build.steps.iter().enumerate() {
// Indicate to user the current step being executed
let current_step = i + 1;
let pb_hdr = format!(
"\nBuilding: step {current_step} of {step_count} {step}"
);
let pb_hdr =
format!("Building: step {current_step} of {step_count} {step}");
let tx = pb.begin_step(pb_hdr);

// Perform build step
Expand Down Expand Up @@ -157,34 +156,45 @@ pub async fn exec(ctx: &Context, args: &BuildArgs) -> Result<(), CommandError> {
&pb,
async { build_result },
|| "Built successfully".to_string(),
|err| format!("Failed to build canister: {err}"),
print_build_error,
)
.await;

// After progress bar is finished, dump the output if build failed
if let Err(e) = &result {
pb.dump_output(ctx);
let _ = ctx
.term
.write_line(&format!("Failed to build canister: {e}"));
}
// If build failed, get the output for later display
let output = if result.is_err() {
Some(pb.dump_output())
} else {
None
};

result
(result, output)
}
};

futs.push_back(fut);
}

// Consume the set of futures and abort if an error occurs
let mut found_error = false;
while let Some(res) = futs.next().await {
if res.is_err() {
found_error = true;
// Consume the set of futures and collect results
let mut failed_outputs = Vec::new();

while let Some((res, output)) = futs.next().await {
if let Err(e) = res
&& let Some(output) = output
{
failed_outputs.push((e, output));
}
}

if found_error {
// If any builds failed, dump the output and abort
if !failed_outputs.is_empty() {
for (e, output) in failed_outputs {
for line in output {
Comment thread
ilbertt marked this conversation as resolved.
let _ = ctx.term.write_line(&line);
}
let _ = ctx.term.write_line(&print_build_error(&e));
let _ = ctx.term.write_line("");
}

return Err(CommandError::Unexpected(anyhow!(
"One or more canisters failed to build"
)));
Expand All @@ -194,3 +204,7 @@ pub async fn exec(ctx: &Context, args: &BuildArgs) -> Result<(), CommandError> {

Ok(())
}

fn print_build_error(err: &CommandError) -> String {
format!("Failed to build canister: {err}")
}
15 changes: 10 additions & 5 deletions crates/icp-cli/src/commands/sync/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,16 +194,17 @@ pub async fn exec(ctx: &Context, args: &SyncArgs) -> Result<(), CommandError> {
&pb,
async { sync_result },
|| format!("Synced successfully: {cid}"),
|err| format!("Failed to sync canister: {err}"),
print_sync_error,
)
.await;

// After progress bar is finished, dump the output if sync failed
if let Err(e) = &result {
pb.dump_output(ctx);
let _ = ctx
.term
.write_line(&format!("Failed to sync canister: {e}"));
for line in pb.dump_output() {
let _ = ctx.term.write_line(&line);
}
let _ = ctx.term.write_line(&print_sync_error(e));
let _ = ctx.term.write_line("");
}

result
Expand Down Expand Up @@ -231,3 +232,7 @@ pub async fn exec(ctx: &Context, args: &SyncArgs) -> Result<(), CommandError> {

Ok(())
}

fn print_sync_error(err: &CommandError) -> String {
format!("Failed to sync canister: {err}")
}
2 changes: 1 addition & 1 deletion crates/icp-cli/src/logging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ impl<W: Write + AsRawFd> Write for TermWriter<W> {
if !self.debug {
self.writer.write(buf)?;
}
debug!("{}", String::from_utf8_lossy(buf));
debug!("{}", String::from_utf8_lossy(buf).trim());
Ok(buf.len())
}

Expand Down
32 changes: 22 additions & 10 deletions crates/icp-cli/src/progress.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ use itertools::Itertools;
use tokio::{sync::mpsc, task::JoinHandle};
use tracing::debug;

use crate::commands::Context;

/// The maximum number of lines to display for a step output
pub const MAX_LINES_PER_STEP: usize = 10_000;

Expand Down Expand Up @@ -253,20 +251,34 @@ impl MultiStepProgressBar {
self.finished_steps.push(StepOutput { title, output });
}

pub fn dump_output(&self, ctx: &Context) {
let _ = ctx.term.write_line(&format!(
"{} output for canister {}:",
self.output_label, self.canister_name
pub fn dump_output(&self) -> Vec<String> {
let mut lines = Vec::new();

lines.push(format!(
"[{}] {} output:",
self.canister_name, self.output_label
));

for step_output in self.finished_steps.iter() {
let _ = ctx.term.write_line(&step_output.title);
for line in step_output.output.iter() {
let _ = ctx.term.write_line(line);
for line in step_output.title.lines() {
if !line.is_empty() {
lines.push(format!("[{}] {}:", self.canister_name, line));
}
}

if step_output.output.is_empty() {
let _ = ctx.term.write_line("<no output>");
lines.push(format!("[{}] <no output>", self.canister_name));
} else {
lines.extend(
step_output
.output
.iter()
.map(|s| format!("[{}] > {s}", self.canister_name)),
);
}
}

lines
}
}

Expand Down
114 changes: 52 additions & 62 deletions crates/icp-cli/tests/build_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,23 +119,20 @@ fn build_adapter_display_failing_build_output() {

// Invoke build
let expected_output = indoc! {r#"
Build output for canister my-canister:

Building: step 1 of 3 (script)
echo "success 1"
success 1

Building: step 2 of 3 (script)
echo "success 2"
success 2

Building: step 3 of 3 (script)
for i in $(seq 1 5); do echo "failing build step $i"; done; exit 1
failing build step 1
failing build step 2
failing build step 3
failing build step 4
failing build step 5
[my-canister] Build output:
[my-canister] Building: step 1 of 3 (script):
[my-canister] echo "success 1":
[my-canister] > success 1
[my-canister] Building: step 2 of 3 (script):
[my-canister] echo "success 2":
[my-canister] > success 2
[my-canister] Building: step 3 of 3 (script):
[my-canister] for i in $(seq 1 5); do echo "failing build step $i"; done; exit 1:
[my-canister] > failing build step 1
[my-canister] > failing build step 2
[my-canister] > failing build step 3
[my-canister] > failing build step 4
[my-canister] > failing build step 5
Failed to build canister: command 'for i in $(seq 1 5); do echo "failing build step $i"; done; exit 1' failed with status code 1
"#};

Expand Down Expand Up @@ -176,24 +173,22 @@ fn build_adapter_display_failing_prebuilt_output() {

// Invoke build
let expected_output = indoc! {r#"
Build output for canister my-canister:

Building: step 1 of 2 (script)
echo "initial step succeeded"
initial step succeeded

Building: step 2 of 2 (pre-built)
path: /nonexistent/path/to/wasm.wasm, sha: invalid
Reading local file: /nonexistent/path/to/wasm.wasm
[my-canister] Build output:
[my-canister] Building: step 1 of 2 (script):
[my-canister] echo "initial step succeeded":
[my-canister] > initial step succeeded
[my-canister] Building: step 2 of 2 (pre-built):
[my-canister] path: /nonexistent/path/to/wasm.wasm, sha: invalid:
[my-canister] > Reading local file: /nonexistent/path/to/wasm.wasm
Failed to build canister: failed to read prebuilt canister file
"#};

ctx.icp()
.current_dir(project_dir)
.args(["build"])
.assert()
.failure()
.stdout(contains(expected_output))
.stdout(contains("Failed to build canister:"));
.stdout(contains(expected_output));
}

#[test]
Expand Down Expand Up @@ -223,15 +218,13 @@ fn build_adapter_display_failing_build_output_no_output() {

// Invoke build
let expected_output = indoc! {r#"
Build output for canister my-canister:

Building: step 1 of 2 (script)
echo "step 1 succeeded"
step 1 succeeded

Building: step 2 of 2 (script)
exit 1
<no output>
[my-canister] Build output:
[my-canister] Building: step 1 of 2 (script):
[my-canister] echo "step 1 succeeded":
[my-canister] > step 1 succeeded
[my-canister] Building: step 2 of 2 (script):
[my-canister] exit 1:
[my-canister] <no output>
Failed to build canister: command 'exit 1' failed with status code 1
"#};

Expand Down Expand Up @@ -277,28 +270,24 @@ fn build_adapter_display_multiple_failing_canisters() {

// Invoke build
let expected_output_one = indoc! {r#"
Build output for canister canister-one:

Building: step 1 of 2 (script)
echo "canister-one step 1"
canister-one step 1

Building: step 2 of 2 (script)
echo "canister-one error"; exit 1
canister-one error
[canister-one] Build output:
[canister-one] Building: step 1 of 2 (script):
[canister-one] echo "canister-one step 1":
[canister-one] > canister-one step 1
[canister-one] Building: step 2 of 2 (script):
[canister-one] echo "canister-one error"; exit 1:
[canister-one] > canister-one error
Failed to build canister: command 'echo "canister-one error"; exit 1' failed with status code 1
"#};

let expected_output_two = indoc! {r#"
Build output for canister canister-two:

Building: step 1 of 2 (script)
echo "canister-two step 1"
canister-two step 1

Building: step 2 of 2 (script)
echo "canister-two error"; exit 1
canister-two error
[canister-two] Build output:
[canister-two] Building: step 1 of 2 (script):
[canister-two] echo "canister-two step 1":
[canister-two] > canister-two step 1
[canister-two] Building: step 2 of 2 (script):
[canister-two] echo "canister-two error"; exit 1:
[canister-two] > canister-two error
Failed to build canister: command 'echo "canister-two error"; exit 1' failed with status code 1
"#};

Expand Down Expand Up @@ -380,13 +369,14 @@ fn build_adapter_display_script_multiple_commands_output() {

// Invoke build
let expected_output = indoc! {r#"
Building: step 1 of 1 (script)
echo "command 1"
echo "command 2"
echo "command 3"
command 1
command 2
command 3
[my-canister] Build output:
[my-canister] Building: step 1 of 1 (script):
[my-canister] echo "command 1":
[my-canister] echo "command 2":
[my-canister] echo "command 3":
[my-canister] > command 1
[my-canister] > command 2
[my-canister] > command 3
Failed to build canister: build did not result in output
"#};

Expand Down