Skip to content

Commit 8cddf9c

Browse files
fix(cli): Add timeout to build upload polling loop
The polling loop in `build upload` could infinite loop when the server returns a pending state (Created/Assembling) without an artifact_url. Add a 5-minute timeout and 1-second sleep between iterations, matching the pattern used in the existing poll_assemble function. Fixes #2942 Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 50091ba commit 8cddf9c

1 file changed

Lines changed: 18 additions & 5 deletions

File tree

src/commands/build/upload.rs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use std::borrow::Cow;
22
use std::io::Write as _;
33
use std::path::Path;
4+
use std::thread;
5+
use std::time::Instant;
46

57
use anyhow::{anyhow, bail, Context as _, Result};
68
use clap::{Arg, ArgAction, ArgMatches, Command};
@@ -13,13 +15,14 @@ use zip::{DateTime, ZipWriter};
1315

1416
use crate::api::{Api, AuthenticatedApi, ChunkedBuildRequest, ChunkedFileState, VcsInfo};
1517
use crate::config::Config;
18+
use crate::constants::DEFAULT_MAX_WAIT;
1619
use crate::utils::args::ArgExt as _;
1720
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
1821
use crate::utils::build::{handle_asset_catalogs, ipa_to_xcarchive, is_apple_app, is_ipa_file};
1922
use crate::utils::build::{
2023
is_aab_file, is_apk_file, is_zip_file, normalize_directory, write_version_metadata,
2124
};
22-
use crate::utils::chunks::{upload_chunks, Chunk};
25+
use crate::utils::chunks::{upload_chunks, Chunk, ASSEMBLE_POLL_INTERVAL};
2326
use crate::utils::ci::is_ci;
2427
use crate::utils::fs::get_sha1_checksums;
2528
use crate::utils::fs::TempDir;
@@ -654,7 +657,9 @@ fn upload_file(
654657
// iteration of the loop) we get:
655658
// n. state=error, artifact_url unset
656659

657-
let result = loop {
660+
let assemble_start = Instant::now();
661+
662+
loop {
658663
let response = api.assemble_build(
659664
org,
660665
project,
@@ -685,15 +690,23 @@ fn upload_file(
685690
}
686691

687692
if let Some(artifact_url) = response.artifact_url {
688-
break Ok(artifact_url);
693+
return Ok(artifact_url);
689694
}
690695

691696
if response.state.is_finished() {
692697
bail!("File upload is_finished() but did not succeeded or error");
693698
}
694-
};
695699

696-
result
700+
// Check for timeout to prevent infinite loop
701+
if assemble_start.elapsed() > DEFAULT_MAX_WAIT {
702+
bail!(
703+
"Timeout waiting for build assembly after {} seconds",
704+
DEFAULT_MAX_WAIT.as_secs()
705+
);
706+
}
707+
708+
thread::sleep(ASSEMBLE_POLL_INTERVAL);
709+
}
697710
}
698711

699712
/// Utility function to parse a SHA1 digest, allowing empty strings.

0 commit comments

Comments
 (0)