Author SHA1 Message Date
Omar Sobh 66b391863d claw-cargo: map cargo profile name to actual target dir
Cargo aliases the `dev`/`test` profiles to `target/debug/` and
`release`/`bench` to `target/release/`. claw-cargo was using the
profile name verbatim as the subdirectory, so with the default
`profile = "dev"` config it looked for `target/dev/` — a directory
that never exists — and silently skipped the capture+upload step
after a successful cold build.

Symptom in the field (pilot deploy on tank, 2026-07-12):

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 12.22s
    cargo build finished in 12.2461178s
    target dir ./target/dev does not exist after cargo build — skipping upload

Fix: `target_subdir_for(profile)` maps aliases to their real dirs;
custom profile names pass through unchanged. Applied in `cmd_build`
and `cmd_prefetch`. Cheap unit test guards the mapping so the same
bug can't come back.
2026-07-12 05:11:50 -07:00
+35 -2
View File
@@ -365,7 +365,7 @@ async fn cmd_status(args: PeerArgs) -> Result<()> {
async fn cmd_prefetch(args: PrefetchArgs) -> Result<()> { async fn cmd_prefetch(args: PrefetchArgs) -> Result<()> {
let (workspace, resolved, fp) = setup_peer(&args.peer)?; let (workspace, resolved, fp) = setup_peer(&args.peer)?;
let target_dir = workspace.join("target").join(&resolved.profile); let target_dir = workspace.join("target").join(target_subdir_for(&resolved.profile));
let (client, conn) = connect_peer(&resolved).await?; let (client, conn) = connect_peer(&resolved).await?;
@@ -485,7 +485,7 @@ async fn resolve_pin(
async fn cmd_build(args: BuildArgs) -> Result<()> { async fn cmd_build(args: BuildArgs) -> Result<()> {
let (workspace, resolved, fp) = setup_peer(&args.peer)?; let (workspace, resolved, fp) = setup_peer(&args.peer)?;
let target_dir = workspace.join("target").join(&resolved.profile); let target_dir = workspace.join("target").join(target_subdir_for(&resolved.profile));
tracing::info!("fingerprint {}", fp); tracing::info!("fingerprint {}", fp);
let (client, conn) = connect_peer(&resolved).await?; let (client, conn) = connect_peer(&resolved).await?;
@@ -911,6 +911,18 @@ async fn cmd_prewarm(args: PrewarmArgs) -> Result<()> {
Ok(()) Ok(())
} }
/// Map a cargo profile name to the directory under `target/` cargo
/// actually writes to. Cargo aliases `dev`/`test` to `debug/` and
/// `release`/`bench` to `release/`; every other (custom) profile
/// gets a directory of the same name.
fn target_subdir_for(profile: &str) -> &str {
match profile {
"dev" | "test" => "debug",
"release" | "bench" => "release",
other => other,
}
}
fn run_cargo( fn run_cargo(
workspace: &Path, workspace: &Path,
profile: &str, profile: &str,
@@ -928,3 +940,24 @@ fn run_cargo(
let status = cmd.status().context("spawning cargo build")?; let status = cmd.status().context("spawning cargo build")?;
Ok(status) Ok(status)
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn target_subdir_matches_cargo_layout() {
// Cargo's built-in profile aliases: dev + test both compile
// into `target/debug/`; release + bench compile into
// `target/release/`. Custom profiles get a dir of the same
// name. Getting this wrong silently skips the capture step
// ("target dir does not exist after cargo build") — the exact
// failure mode we hit in the field on the first pilot run.
assert_eq!(target_subdir_for("dev"), "debug");
assert_eq!(target_subdir_for("test"), "debug");
assert_eq!(target_subdir_for("release"), "release");
assert_eq!(target_subdir_for("bench"), "release");
assert_eq!(target_subdir_for("prod"), "prod");
assert_eq!(target_subdir_for("hot-loop"), "hot-loop");
}
}