Etapa G: soporte go.work multimódulo (vendor_go_deps usa 'go work vendor')
Destraba la clase workspace (go-mockery/k3d/git-town/csvtk): con go.work, GOWORK=off + 'go mod vendor' sólo vendoreaba el módulo raíz → 'inconsistent vendoring' en el build. Ahora si hay go.work se usa 'go work vendor' (go 1.22+, vendor consistente de todo el workspace); el trigger y detect_build_system también reconocen go.work (workspace sin go.mod raíz). Test detect_go_when_only_go_work.
This commit is contained in:
@@ -246,26 +246,38 @@ pub fn vendor_cargo_deps(src: &Path) -> hammer_core::Result<()> {
|
|||||||
///
|
///
|
||||||
/// Un proyecto sin deps externas produce un `vendor/` vacío (o ninguno): inocuo.
|
/// Un proyecto sin deps externas produce un `vendor/` vacío (o ninguno): inocuo.
|
||||||
pub fn vendor_go_deps(src: &Path) -> hammer_core::Result<()> {
|
pub fn vendor_go_deps(src: &Path) -> hammer_core::Result<()> {
|
||||||
let output = Command::new("go")
|
// Proyecto WORKSPACE (multimódulo): si el árbol trae `go.work`, `go mod vendor` con GOWORK=off
|
||||||
.current_dir(src)
|
// vendorea SÓLO el módulo raíz → el build (que ve todo el workspace) se queja de "inconsistent
|
||||||
.args(["mod", "vendor"])
|
// vendoring". `go work vendor` (go 1.22+) produce un `vendor/` consistente para TODO el workspace.
|
||||||
|
// Destraba la clase go.work (go-mockery, k3d, git-town, csvtk…). Sin go.work, el camino clásico
|
||||||
|
// `go mod vendor` con GOWORK=off (ignora cualquier go.work espurio y vendorea el raíz).
|
||||||
|
let has_work = src.join("go.work").is_file();
|
||||||
|
let (verb, label): ([&str; 2], &str) = if has_work {
|
||||||
|
(["work", "vendor"], "go work vendor")
|
||||||
|
} else {
|
||||||
|
(["mod", "vendor"], "go mod vendor")
|
||||||
|
};
|
||||||
|
let mut cmd = Command::new("go");
|
||||||
|
cmd.current_dir(src)
|
||||||
|
.args(verb)
|
||||||
.env("GOFLAGS", "-mod=mod")
|
.env("GOFLAGS", "-mod=mod")
|
||||||
.env("GOTOOLCHAIN", "local")
|
.env("GOTOOLCHAIN", "local");
|
||||||
// `go mod vendor` aborta en modo workspace (si el árbol trae go.work). Lo apagamos para
|
if !has_work {
|
||||||
// vendorear contra el go.mod del módulo raíz (que es lo que el build compila).
|
cmd.env("GOWORK", "off");
|
||||||
.env("GOWORK", "off")
|
}
|
||||||
|
let output = cmd
|
||||||
.stdout(Stdio::inherit())
|
.stdout(Stdio::inherit())
|
||||||
.stderr(Stdio::inherit())
|
.stderr(Stdio::inherit())
|
||||||
.output()
|
.output()
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
hammer_core::Error::Other(anyhow::anyhow!(
|
hammer_core::Error::Other(anyhow::anyhow!(
|
||||||
"spawn go mod vendor: {e} (¿está `go` en el PATH del host? lo provee el lab, \
|
"spawn {label}: {e} (¿está `go` en el PATH del host? lo provee el lab, \
|
||||||
cf. scripts/bootstrap-devfs.sh / vps-setup.sh)"
|
cf. scripts/bootstrap-devfs.sh / vps-setup.sh)"
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
return Err(hammer_core::Error::Other(anyhow::anyhow!(
|
return Err(hammer_core::Error::Other(anyhow::anyhow!(
|
||||||
"go mod vendor falló (exit {:?})",
|
"{label} falló (exit {:?})",
|
||||||
output.status.code()
|
output.status.code()
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ pub fn build(
|
|||||||
// aporta `deps.build = ["go"]` (recipes/go.toml) DENTRO del sandbox; el vendoring usa el `go`
|
// aporta `deps.build = ["go"]` (recipes/go.toml) DENTRO del sandbox; el vendoring usa el `go`
|
||||||
// del host (lo provee el lab). No hay BuildSys::Go: las recetas Go fijan el compile en
|
// del host (lo provee el lab). No hay BuildSys::Go: las recetas Go fijan el compile en
|
||||||
// `[build.phases]` (ver tandas/staged-2026-06-26/amfora.toml como plantilla).
|
// `[build.phases]` (ver tandas/staged-2026-06-26/amfora.toml como plantilla).
|
||||||
if src_tree.join("go.mod").is_file() {
|
if src_tree.join("go.mod").is_file() || src_tree.join("go.work").is_file() {
|
||||||
tracing::info!("go: vendoreando módulos para build offline");
|
tracing::info!("go: vendoreando módulos para build offline");
|
||||||
fetch::vendor_go_deps(&src_tree)?;
|
fetch::vendor_go_deps(&src_tree)?;
|
||||||
}
|
}
|
||||||
@@ -443,10 +443,11 @@ fn ensure_cargo_workspace_isolation(src: &Path) -> hammer_core::Result<()> {
|
|||||||
|
|
||||||
fn detect_build_system(src: &Path) -> BuildSys {
|
fn detect_build_system(src: &Path) -> BuildSys {
|
||||||
let has = |p: &str| src.join(p).exists();
|
let has = |p: &str| src.join(p).exists();
|
||||||
// `go.mod` PRIMERO: un módulo Go suele traer también un `configure`/`Makefile` AUXILIAR (para
|
// `go.mod`/`go.work` PRIMERO: un módulo Go suele traer también un `configure`/`Makefile` AUXILIAR
|
||||||
// man pages, completions, packaging), pero el binario se hace con `go build`. Ningún paquete
|
// (man pages, completions, packaging), pero el binario se hace con `go build`. Ningún paquete
|
||||||
// C/Rust del corpus trae go.mod, así que esto sólo captura proyectos Go (miller, etc.).
|
// C/Rust del corpus trae go.mod. `go.work` cubre los WORKSPACE multimódulo cuya RAÍZ no tiene
|
||||||
if has("go.mod") {
|
// go.mod (sólo go.work + submódulos) — sin esto caían a Makefile/Unknown.
|
||||||
|
if has("go.mod") || has("go.work") {
|
||||||
return BuildSys::Go;
|
return BuildSys::Go;
|
||||||
}
|
}
|
||||||
// El orden importa: cuando coexisten varios (un tarball release de autotools trae el
|
// El orden importa: cuando coexisten varios (un tarball release de autotools trae el
|
||||||
@@ -836,6 +837,16 @@ commit = "deadbeef"
|
|||||||
assert_eq!(p.install.as_deref(), Some("true"));
|
assert_eq!(p.install.as_deref(), Some("true"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn detect_go_when_only_go_work() {
|
||||||
|
// workspace multimódulo sin go.mod en la raíz (sólo go.work) ⇒ sigue siendo Go (antes caía a
|
||||||
|
// Makefile/Unknown). El vendoring usará `go work vendor`.
|
||||||
|
let d = tempfile::tempdir().unwrap();
|
||||||
|
touch(d.path(), "go.work");
|
||||||
|
touch(d.path(), "Makefile"); // un Makefile auxiliar no debe ganarle a Go
|
||||||
|
assert_eq!(detect_build_system(d.path()), BuildSys::Go);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn detect_go_main_picks_cmd_over_docs() {
|
fn detect_go_main_picks_cmd_over_docs() {
|
||||||
// main real en cmd/mlr + main de ejemplo roto en docs/ ⇒ elige cmd/mlr, ignora docs.
|
// main real en cmd/mlr + main de ejemplo roto en docs/ ⇒ elige cmd/mlr, ignora docs.
|
||||||
|
|||||||
Reference in New Issue
Block a user