diff --git a/crates/hammer-build/src/sandbox.rs b/crates/hammer-build/src/sandbox.rs index a33e1f9b..efabadfa 100644 --- a/crates/hammer-build/src/sandbox.rs +++ b/crates/hammer-build/src/sandbox.rs @@ -209,13 +209,18 @@ impl Sandbox { for d in &self.deps { d.hash(&mut h); } - // Junto al store para compartir filesystem (hardlinks no cruzan fs). El store suele estar en - // `/store`; ubicamos el merge en `/.dmerge/`. Derivamos del primer dep. + // DENTRO del store (el padre INMEDIATO de las deps), no en `/../`: los hardlinks NO + // cruzan filesystems, y el store puede ser un mount aparte (en el worker es un VOLUMEN + // bind-montado: `/opt/hammer/store` en /dev/sdb, `/opt/hammer` en /dev/sda1). Con + // `.parent().parent()` el merge caía en la raíz ⇒ `cp -al` cross-device fallaba ⇒ fallback al + // apilado directo ⇒ overflow del lowerdir en kio (5315B > 4096B) y build imposible. El padre + // inmediato de un dep ES el store, siempre mismo fs que las deps. `.dmerge` (dot-prefix) no + // matchea `-` ⇒ invisible para el store lookup y build-state, como `.times`. let base = self .deps .first() .and_then(|d| std::fs::canonicalize(d).ok()) - .and_then(|d| d.parent().and_then(|p| p.parent()).map(|p| p.to_path_buf())) + .and_then(|d| d.parent().map(|p| p.to_path_buf())) .unwrap_or_else(|| PathBuf::from(".")); let merged = base.join(".dmerge").join(format!("{:x}", h.finish())); let done = merged.join(".done"); diff --git a/scripts/farm/cosecha-cron.sh b/scripts/farm/cosecha-cron.sh index 0c987362..a1312c6a 100755 --- a/scripts/farm/cosecha-cron.sh +++ b/scripts/farm/cosecha-cron.sh @@ -68,7 +68,7 @@ else continue fi # 2. RECOGE: store sellado worker→laptop (CAS, merge seguro). - if rsync -az -e "$SSH" "root@$ip:$REMOTE/store/" "$ROOT/store/" 2>&1 | tail -2; then + if rsync -az --exclude /.dmerge -e "$SSH" "root@$ip:$REMOTE/store/" "$ROOT/store/" 2>&1 | tail -2; then echo " cosecha ✓" else echo " ⚠ cosecha falló — sigo"