Skip to content

Commit 46dfdba

Browse files
committed
apple-codesign: survive network brownout
Notarization performs a lot of polling requests. Some may fail due to network problems. Only the last request result is required for succefull notarization. Therefore we can safely ignore non-fatal errors until timeout is exceded. Fixes #169
1 parent 2312b1e commit 46dfdba

File tree

2 files changed

+55
-15
lines changed

2 files changed

+55
-15
lines changed

.devcontainer/devcontainer.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"image": "mcr.microsoft.com/devcontainers/universal:2",
3+
"features": {
4+
"ghcr.io/devcontainers/features/rust:1": {}
5+
}
6+
}

apple-codesign/src/notarization.rs

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use {
2222
log::warn,
2323
sha2::Digest,
2424
std::{
25+
error::Error,
2526
fs::File,
2627
io::{Read, Seek, SeekFrom, Write},
2728
path::{Path, PathBuf},
@@ -381,27 +382,34 @@ impl Notarizer {
381382
let start_time = std::time::Instant::now();
382383

383384
loop {
384-
let status = self.get_submission(submission_id)?;
385-
386-
let elapsed = start_time.elapsed();
387-
388-
warn!(
389-
"poll state after {}s: {:?}",
390-
elapsed.as_secs(),
391-
status.data.attributes.status
392-
);
393-
394-
if status.data.attributes.status != notary_api::SubmissionResponseStatus::InProgress {
395-
warn!("Notary API Server has finished processing the uploaded asset");
396-
397-
return Ok(status);
385+
match self.get_submission(submission_id) {
386+
Ok(status) => {
387+
let elapsed = start_time.elapsed();
388+
389+
warn!(
390+
"poll state after {}s: {:?}",
391+
elapsed.as_secs(),
392+
status.data.attributes.status
393+
);
394+
395+
if status.data.attributes.status != notary_api::SubmissionResponseStatus::InProgress {
396+
warn!("Notary API Server has finished processing the uploaded asset");
397+
398+
return Ok(status);
399+
}
400+
},
401+
Err(e) => {
402+
if !is_transient_error(&e) {
403+
return Err(e)
404+
}
405+
}
398406
}
399407

408+
let elapsed = start_time.elapsed();
400409
if elapsed >= wait_limit {
401410
warn!("reached wait limit after {}s", elapsed.as_secs());
402411
return Err(AppleCodesignError::NotarizeWaitLimitReached);
403412
}
404-
405413
std::thread::sleep(self.wait_poll_interval);
406414
}
407415
}
@@ -441,3 +449,29 @@ impl Notarizer {
441449
Ok(self.client()?.list_submissions()?)
442450
}
443451
}
452+
453+
fn is_transient_error(root: &(dyn Error + 'static)) -> bool {
454+
if let Some::<&reqwest::Error>(reqwest_error) = root.downcast_ref::<reqwest::Error>() {
455+
if reqwest_error.is_timeout() {
456+
warn(reqwest_error, 0);
457+
return true;
458+
}
459+
if reqwest_error.is_connect() {
460+
warn(reqwest_error, 0);
461+
return true;
462+
}
463+
}
464+
465+
if let Some(source) = root.source() {
466+
return is_transient_error(source)
467+
}
468+
469+
return false
470+
}
471+
472+
fn warn(error: &(dyn Error + 'static), indent: usize) {
473+
warn!("{}{}", " ".repeat(indent), error);
474+
if let Some(source) = error.source() {
475+
warn(source, indent + 1);
476+
}
477+
}

0 commit comments

Comments
 (0)