fix: post-hook now runs when game binary does not exist
validate_launch() rejects missing executables in build_plan(), causing an early return via `?` before the post-hook block was ever reached. Fix by separating the dry-run path (still fails fast with `?`) from the real-launch path, where build_plan() result is captured without `?` and chained into execute_wait() via and_then(). The post-hook fires for both plan-validation failures and execute_wait() spawn failures. Also skips the pre-launch hook when the plan is invalid (no binary to set up for), and skips state recording in the same case. Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
+23
-16
@@ -1421,16 +1421,10 @@ fn launch_command(
|
||||
let resolved = profile::resolve(config, &executable)?;
|
||||
let verbose = resolved.settings.verbose;
|
||||
let settings = resolved.settings.clone();
|
||||
let plan = launch::build_plan(command, settings.clone())?;
|
||||
let log_path = settings.log_file.then(|| {
|
||||
settings
|
||||
.log_path
|
||||
.as_deref()
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|| crate::log::default_log_path(&paths.state_dir))
|
||||
});
|
||||
|
||||
// Dry-run: build the plan (fail-fast) and render it without launching.
|
||||
if dry_run {
|
||||
let plan = launch::build_plan(command, settings.clone())?;
|
||||
println!(
|
||||
"{}",
|
||||
launch::render_plan(&plan, &resolved.profile_name, verbose)
|
||||
@@ -1459,6 +1453,16 @@ fn launch_command(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Real launch: capture plan errors so the post-hook can always run.
|
||||
let plan_result = launch::build_plan(command, settings.clone());
|
||||
let log_path = settings.log_file.then(|| {
|
||||
settings
|
||||
.log_path
|
||||
.as_deref()
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|| crate::log::default_log_path(&paths.state_dir))
|
||||
});
|
||||
|
||||
// Log the launch header.
|
||||
if let Some(log_path) = &log_path {
|
||||
crate::log::append(
|
||||
@@ -1467,13 +1471,15 @@ fn launch_command(
|
||||
"--- launch ---".to_string(),
|
||||
format!("executable: {}", executable.basename),
|
||||
format!("profile: {}", resolved.profile_name),
|
||||
format!("command: {}", format_command(&plan.command)),
|
||||
format!("command: {}", format_command(command)),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// Run pre-launch hook.
|
||||
if let Some(pre_cmd) = settings.pre_launch.as_deref() {
|
||||
// Run pre-launch hook (only when the plan is valid; skip if binary not found).
|
||||
if plan_result.is_ok()
|
||||
&& let Some(pre_cmd) = settings.pre_launch.as_deref()
|
||||
{
|
||||
let hook_result = run_hook(pre_cmd);
|
||||
let log_label = match &hook_result {
|
||||
Ok(status) => exit_status_label(*status),
|
||||
@@ -1507,16 +1513,16 @@ fn launch_command(
|
||||
}
|
||||
}
|
||||
|
||||
// Record the launch in state (Steam context only).
|
||||
if env::is_steam_context() {
|
||||
// Record the launch in state (Steam context only, when plan is valid).
|
||||
if plan_result.is_ok() && env::is_steam_context() {
|
||||
detect::record_launch(state, &executable, &resolved.profile_name);
|
||||
config::save_state(paths, state)?;
|
||||
}
|
||||
|
||||
if let Some(post_cmd) = settings.post_launch.clone() {
|
||||
// Spawn the game and wait. Capture both success and failure so the
|
||||
// post-hook can always run once we've passed the pre-hook check.
|
||||
let game_result = launch::execute_wait(plan);
|
||||
// Chain plan building with game execution so post-hook runs even when
|
||||
// the binary doesn't exist or otherwise fails to spawn.
|
||||
let game_result = plan_result.and_then(launch::execute_wait);
|
||||
|
||||
let (elapsed_secs, game_exit_label) = match &game_result {
|
||||
Ok((exit_status, elapsed)) => {
|
||||
@@ -1579,6 +1585,7 @@ fn launch_command(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let plan = plan_result?;
|
||||
launch::execute(plan)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user