> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…
Etapa G: detect_go_main prefiere cmd/<nombre-receta> entre varios mains
eksctl tiene cmd/eksctl (CLI) + cmd/schema (helper de codegen que panica); detect elegía
arbitrariamente y selló el binario equivocado (schema) — un fallo SILENCIOSO (sella OK pero envía
la herramienta incorrecta). Ahora, a igual clase, el dir cuyo file_name == recipe.name gana. Test
detect_go_main_prefers_cmd_matching_recipe_name. Lección: smoke-testear que el binario instalado es
el ESPERADO, no solo que selló.
diff --git a/crates/hammer-build/src/lib.rs b/crates/hammer-build/src/lib.rs
index 7539822f..a34d3f36 100644
--- a/crates/hammer-build/src/lib.rs
+++ b/crates/hammer-build/src/lib.rs
@@ -485,7 +485,7 @@ fn detect_build_system(src: &Path) -> BuildSys {
/// scripts/tools/internal y ocultos/`_`). Elige el principal por heurística: raíz > `cmd/<x>` >
/// menor profundidad. Devuelve un path estilo go (`.`, `./cmd/mlr`). Una receta puede forzar el
/// paquete vía `build.flags` (gana sobre la auto-detección).
-fn detect_go_main(src: &Path) -> String {
+fn detect_go_main(src: &Path, pkg_name: &str) -> String {
fn is_main_go(path: &Path) -> bool {
let Ok(text) = std::fs::read_to_string(path) else { return false };
// El primer token significativo de un .go es la cláusula `package` (tras build-constraints
@@ -562,17 +562,22 @@ fn detect_go_main(src: &Path) -> String {
}
}
}
- // raíz (prioridad 0) > cmd/<x> (1) > resto (2); a igual clase, menor profundidad.
- let key = |p: &PathBuf| -> (u8, usize) {
+ // raíz (prioridad 0) > cmd/<x> (1) > resto (2); a igual clase, el dir cuyo nombre coincide con el
+ // de la receta (p.ej. cmd/eksctl para la receta `eksctl`) gana sobre otros mains hermanos (cmd/
+ // schema, un helper de codegen que panica) — sin esto detect elegía arbitrariamente entre varios
+ // mains y sellaba el binario EQUIVOCADO; luego, a igual nombre, menor profundidad.
+ let key = |p: &PathBuf| -> (u8, u8, usize) {
let s = p.to_string_lossy();
let depth = p.components().count();
- if s.is_empty() {
- (0, depth)
+ let name_match = if p.file_name().map(|f| f == pkg_name).unwrap_or(false) { 0 } else { 1 };
+ let class = if s.is_empty() {
+ 0
} else if s.starts_with("cmd/") || s == "cmd" {
- (1, depth)
+ 1
} else {
- (2, depth)
- }
+ 2
+ };
+ (class, name_match, depth)
};
found.sort_by_key(key);
match found.first() {
@@ -740,7 +745,7 @@ fn resolve_phases(recipe: &Recipe, src: &Path) -> hammer_core::Result<Phases> {
// el nombre. Offline+reproducible: vendor (vendor_go_deps) + GOPROXY=off + -trimpath
// -buildid=. El toolchain `go` llega por deps.build=["go"] (overlay).
let pkg = if recipe.build.flags.is_empty() {
- detect_go_main(src)
+ detect_go_main(src, &recipe.name)
} else {
flags.clone()
};
@@ -839,7 +844,19 @@ commit = "deadbeef"
std::fs::create_dir_all(d.path().join(sub)).unwrap();
std::fs::write(d.path().join(sub).join("main.go"), b"package main\nfunc main(){}\n").unwrap();
}
- assert_eq!(detect_go_main(d.path()), "./cmd/mlr");
+ assert_eq!(detect_go_main(d.path(), "mlr"), "./cmd/mlr");
+ }
+
+ #[test]
+ fn detect_go_main_prefers_cmd_matching_recipe_name() {
+ // eksctl-like: cmd/eksctl (CLI real) + cmd/schema (helper que panica). El dir que coincide
+ // con el name de la receta gana sobre el hermano ⇒ no sella el binario equivocado.
+ let d = tempfile::tempdir().unwrap();
+ for sub in ["cmd/schema", "cmd/eksctl"] {
+ std::fs::create_dir_all(d.path().join(sub)).unwrap();
+ std::fs::write(d.path().join(sub).join("main.go"), b"package main\nfunc main(){}\n").unwrap();
+ }
+ assert_eq!(detect_go_main(d.path(), "eksctl"), "./cmd/eksctl");
}
#[test]
@@ -853,7 +870,7 @@ commit = "deadbeef"
b"/*\nCopyright The Helm Authors.\nLicensed under the Apache License 2.0\n*/\n\npackage main\n\nfunc main() {}\n",
)
.unwrap();
- assert_eq!(detect_go_main(d.path()), "./cmd/helm");
+ assert_eq!(detect_go_main(d.path(), "helm"), "./cmd/helm");
}
#[test]