#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn main() {
use std::time::Duration;
use supermachine::{Image, VmConfig};
let src = std::env::var("SUPERMACHINE_BAKE_SRC").expect("SUPERMACHINE_BAKE_SRC");
let dest = std::env::var("SUPERMACHINE_BAKE_DEST").expect("SUPERMACHINE_BAKE_DEST");
let mem: u32 = std::env::var("SUPERMACHINE_BAKE_MEM_MIB")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(8192);
let _ = std::fs::remove_dir_all(&dest);
let cfg = VmConfig::new().with_memory_mib(mem);
let vm = Image::from_snapshot(&src)
.expect("from_snapshot")
.start(&cfg)
.expect("start");
for _ in 0..120 {
if let Ok(o) = vm
.exec_builder()
.argv(["/bin/sh", "-c", "echo up"])
.output()
{
if o.success() {
break;
}
}
std::thread::sleep(Duration::from_millis(1000));
}
let run = |cmd: &str| -> String {
let mut last_err = String::new();
let mut out = None;
for attempt in 0..4 {
match vm.exec_builder().argv(["/bin/sh", "-c", cmd]).output() {
Ok(o) => {
out = Some(o);
break;
}
Err(e) => {
last_err = format!("{e:?}");
eprintln!("BAKE exec retry {attempt} after: {last_err}");
std::thread::sleep(std::time::Duration::from_secs(3));
}
}
}
let o = out.unwrap_or_else(|| panic!("exec failed after retries: {last_err}"));
if !o.success() {
panic!(
"guest cmd failed: {cmd}\nstdout: {}\nstderr: {}",
String::from_utf8_lossy(&o.stdout),
String::from_utf8_lossy(&o.stderr)
);
}
String::from_utf8_lossy(&o.stdout).to_string()
};
let pw = std::env::var("SUPERMACHINE_BAKE_PW_VERSION").unwrap_or_else(|_| "1.57".into());
eprintln!("BAKE kernel={}", run("uname -r").trim());
eprintln!("BAKE step=install_chromium (in-guest download, playwright@{pw})");
let out = run(&format!(
"cd /root && npx --yes playwright@{pw} install chromium chromium-headless-shell > /root/bake-install.log 2>&1; \
echo INSTALL_EXIT=$?; tail -3 /root/bake-install.log; \
ls /root/.cache/ms-playwright/ /.cache/ms-playwright/ 2>/dev/null | head -6"
));
eprintln!("BAKE step=install_system_deps");
let deps = run(&format!("cd /root && (npx --yes playwright@{pw} install-deps chromium 2>&1 | tail -2) || (apt-get update -qq && apt-get install -y -qq libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm1 libasound2 libpango-1.0-0 libcairo2 2>&1 | tail -2)"));
eprintln!("BAKE deps_out={}", deps.trim());
eprintln!("BAKE install_out={}", out.trim());
assert!(
out.contains("INSTALL_EXIT=0"),
"playwright install failed inside guest:\n{out}"
);
let mut chrome = String::new();
for attempt in 0..4 {
let found = run("echo FIND_BEGIN; find /root/.cache/ms-playwright /.cache/ms-playwright -maxdepth 4 -name chrome -path '*chrome-linux*' 2>/dev/null | head -1; echo FIND_END");
if !found.contains("FIND_BEGIN") || !found.contains("FIND_END") {
eprintln!("BAKE find retry {attempt}: sentinel missing (exec wedge)");
std::thread::sleep(Duration::from_secs(3));
continue;
}
chrome = found
.split("FIND_BEGIN")
.nth(1)
.unwrap_or("")
.split("FIND_END")
.next()
.unwrap_or("")
.trim()
.to_string();
break;
}
if chrome.is_empty() {
eprintln!(
"BAKE find_diag={}",
run("ls -laR /root/.cache/ms-playwright /.cache/ms-playwright 2>&1 | head -40").trim()
);
}
assert!(
!chrome.is_empty(),
"chromium binary not found after install"
);
eprintln!("BAKE chrome={chrome}");
run(&format!("ln -sf {chrome} /usr/local/bin/chromium"));
eprintln!("BAKE step=chrome_smoke (oops check)");
run("dmesg -C || true");
run(&format!(
"nohup {chrome} --headless --no-sandbox --disable-gpu --disable-dev-shm-usage \
--disable-quic --disable-background-networking --disable-component-update \
--disable-sync --disable-default-apps --no-first-run \
--remote-debugging-port=9224 --remote-debugging-address=127.0.0.1 \
--user-data-dir=/root/cdp-profile-smoke about:blank > /root/chrome-smoke.log 2>&1 & sleep 6; true"
));
let ready = run("node -e \"require('http').get('http://127.0.0.1:9224/json/version',r=>{console.log('cdp ok',r.statusCode);process.exit(0)}).on('error',e=>{console.log('cdp ERR',e.message);process.exit(1)})\" || echo NOT_READY");
eprintln!("BAKE cdp_smoke={}", ready.trim());
if !ready.contains("cdp ok") {
eprintln!(
"BAKE chrome_log={}",
run("tail -6 /root/chrome-smoke.log 2>/dev/null").trim()
);
}
let oops = run("dmesg | grep -cE 'BUG|Oops|RIP: 0010:0x0' || true");
eprintln!("BAKE oops_lines={}", oops.trim());
if oops.trim() != "0" {
eprintln!(
"BAKE oops_dump={}",
run("dmesg | grep -B2 -A14 -E 'BUG|Oops' | head -80 || true").trim()
);
}
assert!(
ready.contains("cdp ok"),
"chromium devtools did not come up"
);
assert_eq!(
oops.trim(),
"0",
"kernel oops during chromium start — kernel too old?"
);
if let Ok(o) = vm.exec_builder().argv(["/bin/sh", "-c", "pkill -f 'cdp-profile-smok[e]' || true; sleep 1; rm -rf /root/cdp-profile-smoke /root/chrome-smoke.log; true"]).output() {
let _ = o;
} else {
eprintln!("BAKE cleanup skipped (exec wedge) — capturing with smoke leftovers");
}
eprintln!("BAKE step=capture_full_snapshot");
let t = std::time::Instant::now();
vm.snapshot_live(&dest).expect("snapshot_live");
eprintln!("BAKE captured in {:?} -> {dest}", t.elapsed());
vm.stop().ok();
eprintln!("BAKE result=PASS chrome={chrome}");
}
#[cfg(not(all(target_os = "linux", target_arch = "x86_64")))]
fn main() {
eprintln!("linux/x86_64 only");
}