fix(cli): --swap no debe confundir el : de b3: con el separador del rel_path
`--swap make=b3:fbad…` se parseaba como hash=`b3`, rel_path=`fbad…` porque el split del rel_path opcional (`name=hash[:rel_path]`) corría ANTES de quitar el prefijo `b3:`. Resultado: el assemble del builder fallaba con "no existe en el artefacto sellado b3:b3" (lo cazó el primer intento de SWAP_MAKE=1 in-VM, antes de bootear la VM ⇒ cero tiempo de máquina perdido). Fix: quitar `b3:` del hash antes de buscar el `:` del rel_path. Extraje el parseo inline a `parse_swap()` y le puse 4 tests de regresión (b3:+default, hash pelado+ rel explícito, b3:+rel explícito, falta '='). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -590,24 +590,7 @@ fn main() -> anyhow::Result<()> {
|
||||
other => anyhow::bail!("--seed debe ser 'zig' o 'musl-cross-make', no '{other}'"),
|
||||
};
|
||||
let store = hammer_core::Store::open(&cli.store)?;
|
||||
let swaps = swaps
|
||||
.iter()
|
||||
.map(|s| {
|
||||
// name=hash[:rel_path]; rel_path por defecto usr/bin/<name>.
|
||||
let (name, rest) = s
|
||||
.split_once('=')
|
||||
.ok_or_else(|| anyhow::anyhow!("--swap '{s}': falta '=' (name=hash[:rel_path])"))?;
|
||||
let (hash, rel_path) = match rest.split_once(':') {
|
||||
Some((h, p)) => (h, p.to_string()),
|
||||
None => (rest, format!("usr/bin/{name}")),
|
||||
};
|
||||
Ok::<_, anyhow::Error>(hammer_bootstrap::ToolchainSwap {
|
||||
name: name.to_string(),
|
||||
artifact: hammer_core::ArtifactHash::from_hex(hash.trim_start_matches("b3:")),
|
||||
rel_path,
|
||||
})
|
||||
})
|
||||
.collect::<anyhow::Result<Vec<_>>>()?;
|
||||
let swaps = swaps.iter().map(|s| parse_swap(s)).collect::<anyhow::Result<Vec<_>>>()?;
|
||||
let spec = hammer_bootstrap::BuilderSpec {
|
||||
stage1_rootfs: hammer_core::ArtifactHash::from_hex(stage1.trim_start_matches("b3:")),
|
||||
seed_hash: hammer_core::ArtifactHash::from_hex(seed_hash.trim_start_matches("b3:")),
|
||||
@@ -1463,6 +1446,60 @@ fn run_ctl(line: &str, fifo: &std::path::Path) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parsea un `--swap name=hash[:rel_path]` (variante b del builder, SDD 11 §7.2b). El `rel_path`
|
||||
/// por defecto es `usr/bin/<name>`. Quitamos el prefijo `b3:` del hash ANTES de buscar el `:` del
|
||||
/// rel_path opcional — si no, `make=b3:abc` se parsearía como hash=`b3`, rel_path=`abc`.
|
||||
fn parse_swap(s: &str) -> anyhow::Result<hammer_bootstrap::ToolchainSwap> {
|
||||
let (name, rest) = s
|
||||
.split_once('=')
|
||||
.ok_or_else(|| anyhow::anyhow!("--swap '{s}': falta '=' (name=hash[:rel_path])"))?;
|
||||
let rest = rest.trim_start_matches("b3:");
|
||||
let (hash, rel_path) = match rest.split_once(':') {
|
||||
Some((h, p)) => (h, p.to_string()),
|
||||
None => (rest, format!("usr/bin/{name}")),
|
||||
};
|
||||
Ok(hammer_bootstrap::ToolchainSwap {
|
||||
name: name.to_string(),
|
||||
artifact: hammer_core::ArtifactHash::from_hex(hash),
|
||||
rel_path,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod swap_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn swap_with_b3_prefix_keeps_full_hash() {
|
||||
// Regresión: el `:` del prefijo `b3:` no debe confundirse con el separador del rel_path.
|
||||
let s = parse_swap("make=b3:fbad44ac").unwrap();
|
||||
assert_eq!(s.name, "make");
|
||||
assert_eq!(s.artifact.as_str(), "b3:fbad44ac");
|
||||
assert_eq!(s.rel_path, "usr/bin/make", "rel_path por defecto");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn swap_bare_hash_and_explicit_rel_path() {
|
||||
let s = parse_swap("bwrap=abcd:usr/bin/bwrap").unwrap();
|
||||
assert_eq!(s.name, "bwrap");
|
||||
assert_eq!(s.artifact.as_str(), "b3:abcd");
|
||||
assert_eq!(s.rel_path, "usr/bin/bwrap");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn swap_b3_prefix_with_explicit_rel_path() {
|
||||
let s = parse_swap("make=b3:fbad:usr/local/bin/make").unwrap();
|
||||
assert_eq!(s.artifact.as_str(), "b3:fbad");
|
||||
assert_eq!(s.rel_path, "usr/local/bin/make");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn swap_missing_equals_errors() {
|
||||
let err = parse_swap("makeb3:fbad").unwrap_err().to_string();
|
||||
assert!(err.contains("falta '='"), "{err}");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod export_tests {
|
||||
use super::*;
|
||||
|
||||
Reference in New Issue
Block a user