Etapa F dogfood: fix bug de patch en install (.swm con patch + build-dep)
Dogfoodear el corpus entero (build-repo.sh: 55/56 con expected_hash anclado, repo firmado Ed25519) destapó un bug latente en la reconstruccion desde .swm: el path del patch se prefijaba con el catalogo (swm-recipes/) TRES veces, y el install abortaba con 'No such file'. jq es el PRIMER paquete con patch + build-dep que se instala (fd/bwrap no tienen patch) -> nunca se habia ejercido ese camino. Causa: recipe_from_source_patch metia catalog_dir en el path Y synthesize_recipe lo re-unia a base_dir, redundante con el base_dir.join(p) que el lab hace al construir (fetch::apply_patches / Recipe::hash). Fix: source.patches guarda solo el NOMBRE relativo a base_dir; un unico join lo resuelve. El hash usa el CONTENIDO del patch, no el string del path -> el expected_hash anclado sigue casando. Verificado e2e: install jq desde el repo firmado -> trusted -> reproduce desde fuente (hash e770e04d = el anclado) -> patch aplicado -> 18 ficheros hidratados -> registrado en DB -> jq corre. + test de regresion inline_patch_resuelve_con_un_solo_join.
This commit is contained in:
@@ -107,14 +107,18 @@ pub fn recipe_from_source_patch(
|
||||
// Patch bajo un nombre derivado del nombre-de-receta + clave de origen, para no colisionar
|
||||
// entre `{dep}.patch` distintos en el mismo catálogo.
|
||||
let label = name.map(|n| n.to_string()).unwrap_or_else(|| derive_name(target_bin));
|
||||
let patch_path = catalog_dir.join(format!("{label}-{source_key}.patch"));
|
||||
// `source.patches` guarda el NOMBRE relativo a `base_dir` (= catalog_dir); el lab resuelve
|
||||
// `base_dir.join(nombre)` UNA vez al construir (fetch::apply_patches / Recipe hash). Guardar el
|
||||
// path completo acá lo haría re-unir a base_dir aguas abajo (doble/triple prefijo).
|
||||
let filename = format!("{label}-{source_key}.patch");
|
||||
let patch_path = catalog_dir.join(&filename);
|
||||
if let Some(text) = patch {
|
||||
std::fs::write(&patch_path, text.as_bytes())?;
|
||||
} else if let Some(url) = patch_url {
|
||||
tracing::info!(%url, "fetch: descargando patch remoto");
|
||||
crate::download::fetch_url_to_file(url, &patch_path)?;
|
||||
}
|
||||
patches.push(patch_path.to_string_lossy().into_owned());
|
||||
patches.push(filename);
|
||||
}
|
||||
|
||||
let name = name.map(|n| n.to_string()).unwrap_or_else(|| derive_name(target_bin));
|
||||
@@ -194,19 +198,11 @@ flags = []
|
||||
if let Some(n) = strip_components {
|
||||
recipe.source.strip_components = n;
|
||||
}
|
||||
// Resolución de patches relativos al recipe_dir donde acabamos de escribir los inline.
|
||||
// Aceptamos rutas absolutas tal cual: las hemos generado nosotros.
|
||||
recipe.source.patches = patches
|
||||
.into_iter()
|
||||
.map(|p| {
|
||||
let path = Path::new(&p);
|
||||
if path.is_absolute() {
|
||||
p
|
||||
} else {
|
||||
base_dir.join(&p).to_string_lossy().into_owned()
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
// `patches` ya viene como nombres relativos a `base_dir` (o rutas absolutas que generamos
|
||||
// nosotros): el lab los resuelve con `base_dir.join(p)` UNA vez al construir. NO los re-unimos
|
||||
// acá — hacerlo doblaba el prefijo `base_dir` y el join del build lo triplicaba (bug que el
|
||||
// dogfood de `jq`, primer paquete con patch + build-dep instalado, destapó).
|
||||
recipe.source.patches = patches;
|
||||
// Deps por NOMBRE (de la receta original): el lab las resolverá como `{dep}.toml` bajo el
|
||||
// `base_dir` — por eso base_dir = el catálogo (no "/"): los patches son absolutos y no lo
|
||||
// necesitan, pero la resolución de build-deps SÍ. `install` puebla el catálogo con los
|
||||
@@ -314,6 +310,37 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inline_patch_resuelve_con_un_solo_join_de_base_dir() {
|
||||
// Regresión: `source.patches` debe guardar el NOMBRE relativo a base_dir, de modo que el
|
||||
// lab resuelva `base_dir.join(p)` UNA sola vez. El bug doblaba/triplicaba el prefijo
|
||||
// `swm-recipes/` (lo destapó el dogfood de `jq`, primer paquete con patch instalado).
|
||||
let d = tempfile::tempdir().unwrap();
|
||||
let catalog = d.path().join("swm-recipes");
|
||||
let m = Mutation::SourcePatch {
|
||||
repo: None,
|
||||
commit: None,
|
||||
tarball: Some("https://x/jq-1.8.1.tar.gz".into()),
|
||||
sha256: Some("2be64e7129cecb11d5906290eba10af694fb9e3e7f9fc208a311dc33ca837eb0".into()),
|
||||
strip_components: None,
|
||||
patch: Some("--- a\n+++ b\n".into()),
|
||||
patch_url: None,
|
||||
build: fake_swm_build(),
|
||||
target_bin: "/usr/bin/jq".into(),
|
||||
expected_hash: None,
|
||||
deps: Default::default(),
|
||||
};
|
||||
let recipe = recipe_from_source_patch(&m, None, &catalog).unwrap();
|
||||
assert_eq!(recipe.source.patches.len(), 1);
|
||||
let p = &recipe.source.patches[0];
|
||||
// El nombre NO debe llevar embebido el dir del catálogo (eso causaba el doble prefijo).
|
||||
assert!(!p.contains("swm-recipes"), "patch debe ser relativo a base_dir, no llevar el catálogo: {p}");
|
||||
// Y base_dir.join(p) — el ÚNICO join que hace el lab — debe dar el fichero real escrito.
|
||||
let resolved = recipe.base_dir.join(p);
|
||||
assert!(resolved.is_file(), "base_dir.join(patch) debe existir en un solo paso: {}", resolved.display());
|
||||
assert_eq!(std::fs::read(&resolved).unwrap(), b"--- a\n+++ b\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_compiler_and_link() {
|
||||
assert_eq!(parse_compiler("zig-cc").unwrap(), Compiler::ZigCc);
|
||||
|
||||
Reference in New Issue
Block a user