Bound every await in push_to; plain-git checkout for real this time
ci / quality (push) Failing after 3m0s

CI (run 208) showed the Push RPC hanging past the test client's 120s
read timeout: endpoint.connect and the delivery-verification observe
loop had no bounds, so a wedged path hung the socket instead of
failing. Connect now times out at 30s and each observe step at 60s,
producing structured errors that name the stage.

The checkout action is back out: run 212 proved the failure is not the
action's version — the runner execs the action's JS with node inside
the job container, and rust:1 has no node ('exec: "node": executable
file not found'). Plain git needs nothing the container lacks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Bendik Lynghaug
2026-08-16 15:39:58 +02:00
co-authored by Claude Fable 5
parent 9198640bd9
commit b53b5cc000
3 changed files with 49 additions and 13 deletions
+27 -11
View File
@@ -7,6 +7,7 @@
use std::path::Path;
use std::sync::Arc;
use std::time::Duration;
use anyhow::{Context, Result};
use iroh::address_lookup::memory::MemoryLookup;
@@ -170,11 +171,16 @@ impl Transfer {
!self.metered.is_metered(),
"metered connection: refusing to push"
);
let conn = self
.endpoint
.connect(peer, iroh_blobs::ALPN)
.await
.with_context(|| format!("connecting to {peer}"))?;
// Every await here is bounded: this runs inside a socket-API
// request, and a wedged peer must produce a structured error,
// not a hung client connection.
let conn = tokio::time::timeout(
Duration::from_secs(30),
self.endpoint.connect(peer, iroh_blobs::ALPN),
)
.await
.map_err(|_| anyhow::anyhow!("connecting to {peer} timed out"))?
.with_context(|| format!("connecting to {peer}"))?;
let request = match content.format {
BlobFormat::Raw => {
iroh_blobs::protocol::PushRequest::from(GetRequest::blob(content.hash))
@@ -240,7 +246,8 @@ impl Transfer {
}
/// Watch `hash` on the remote end of `conn` until its bitfield
/// reports the blob complete.
/// reports the blob complete. Bounded: the peer has already received
/// the bytes, so verification is bookkeeping, not transfer.
async fn wait_remote_complete(
&self,
conn: &iroh::endpoint::Connection,
@@ -251,13 +258,22 @@ impl Transfer {
iroh_blobs::protocol::ObserveRequest::new(hash),
);
let mut observe = std::pin::pin!(observe);
while let Some(bitfield) = observe.next().await {
let bitfield = bitfield.context("observing pushed content on the peer")?;
if bitfield.is_complete() {
return Ok(());
loop {
let next = tokio::time::timeout(Duration::from_secs(60), observe.next())
.await
.map_err(|_| anyhow::anyhow!("verifying pushed content on the peer timed out"))?;
match next {
Some(bitfield) => {
let bitfield = bitfield.context("observing pushed content on the peer")?;
if bitfield.is_complete() {
return Ok(());
}
}
None => {
anyhow::bail!("peer stopped reporting before the pushed content completed")
}
}
}
anyhow::bail!("peer stopped reporting before the pushed content completed");
}
/// Produce a ticket for out-of-band sharing of `content`.