From 3b23463ac1c42864a7bf2a869409c211047b5744 Mon Sep 17 00:00:00 2001 From: sergio Date: Fri, 26 Jun 2026 23:38:47 -0400 Subject: [PATCH] Etapa G: detect_go_main ve el main bajo header de comentario de bloque MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cmd/helm/helm.go arranca con un header Apache /* ... */ cuyas líneas internas no llevan '*'; el filtro por-línea ingenuo de is_main_go cortaba en 'Copyright ...' y daba 'no es main' ⇒ detect_go_main no hallaba cmd/helm y caía a '.' (raíz sin .go → 'no Go files in /src'). Ahora rastrea el estado dentro/fuera de bloque /* */. Afecta a cualquier .go con header de bloque. Test: detect_go_main_sees_main_under_block_comment_header. --- crates/hammer-build/src/lib.rs | 52 ++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/crates/hammer-build/src/lib.rs b/crates/hammer-build/src/lib.rs index 011155e3..19323680 100644 --- a/crates/hammer-build/src/lib.rs +++ b/crates/hammer-build/src/lib.rs @@ -488,11 +488,45 @@ fn detect_build_system(src: &Path) -> BuildSys { fn detect_go_main(src: &Path) -> 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 + // `//go:build`, comentarios de línea y el header de licencia). Hay que saltar comentarios de + // BLOQUE `/* ... */` correctamente: muchos headers Apache son `/*\nCopyright ...\n*/` y las + // líneas internas NO arrancan con `*` ⇒ un filtro por-línea ingenuo cortaría ahí y fallaría + // (p.ej. cmd/helm/helm.go). Escaneamos rastreando el estado dentro/fuera de bloque. + let mut in_block = false; for line in text.lines() { - let l = line.trim(); - if l.is_empty() || l.starts_with("//") || l.starts_with("/*") || l.starts_with('*') { + let mut l = line.trim(); + if in_block { + match l.find("*/") { + Some(i) => { + l = l[i + 2..].trim(); + in_block = false; + } + None => continue, + } + } + if l.is_empty() || l.starts_with("//") { continue; } + if let Some(i) = l.find("/*") { + let before = l[..i].trim(); + if !before.is_empty() { + return before.starts_with("package main"); + } + match l[i + 2..].find("*/") { + Some(j) => { + let after = l[i + 2 + j + 2..].trim(); + if after.is_empty() { + continue; + } + return after.starts_with("package main"); + } + None => { + in_block = true; + continue; + } + } + } return l.starts_with("package main"); } false @@ -800,6 +834,20 @@ commit = "deadbeef" assert_eq!(detect_go_main(d.path()), "./cmd/mlr"); } + #[test] + fn detect_go_main_sees_main_under_block_comment_header() { + // header de licencia `/* ... */` con líneas internas SIN `*` (estilo Apache de helm): + // el main en cmd/helm debe detectarse igual (regresión del corte ingenuo por-línea). + let d = tempfile::tempdir().unwrap(); + std::fs::create_dir_all(d.path().join("cmd/helm")).unwrap(); + std::fs::write( + d.path().join("cmd/helm/helm.go"), + 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"); + } + #[test] fn detect_autoconf_raw_when_only_configure_ac() { let d = tempfile::tempdir().unwrap();