diff --git a/docs/content/hook.md b/docs/content/hook.md index b61935419d..8d1d3979da 100644 --- a/docs/content/hook.md +++ b/docs/content/hook.md @@ -23,7 +23,7 @@ Hooks are shell commands that run at key points in the worktree lifecycle — au | **merge** | `pre-merge` | `post-merge` | | **remove** | `pre-remove` | `post-remove` | -`pre-*` hooks block — failure aborts the operation (except `pre-start`, which warns and continues). `post-*` hooks run in the background with output logged to `.git/wt/logs/{branch}-{source}-{hook}-{name}.log`. Use `-v` to see expanded command details for background hooks. +`pre-*` hooks block — failure aborts the operation. `post-*` hooks run in the background with output logged to `.git/wt/logs/{branch}-{source}-{hook}-{name}.log`. Use `-v` to see expanded command details for background hooks. The most common starting point is `post-start` — it runs background tasks (dev servers, file copying, builds) when creating a worktree. diff --git a/skills/worktrunk/reference/hook.md b/skills/worktrunk/reference/hook.md index 7ad732919b..4c75d329f9 100644 --- a/skills/worktrunk/reference/hook.md +++ b/skills/worktrunk/reference/hook.md @@ -14,7 +14,7 @@ Hooks are shell commands that run at key points in the worktree lifecycle — au | **merge** | `pre-merge` | `post-merge` | | **remove** | `pre-remove` | `post-remove` | -`pre-*` hooks block — failure aborts the operation (except `pre-start`, which warns and continues). `post-*` hooks run in the background with output logged to `.git/wt/logs/{branch}-{source}-{hook}-{name}.log`. Use `-v` to see expanded command details for background hooks. +`pre-*` hooks block — failure aborts the operation. `post-*` hooks run in the background with output logged to `.git/wt/logs/{branch}-{source}-{hook}-{name}.log`. Use `-v` to see expanded command details for background hooks. The most common starting point is `post-start` — it runs background tasks (dev servers, file copying, builds) when creating a worktree. diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 625f37ce17..0d4ceaa53d 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -1114,7 +1114,7 @@ Alias names that match a built-in step command (`commit`, `squash`, etc.) are sh | **merge** | `pre-merge` | `post-merge` | | **remove** | `pre-remove` | `post-remove` | -`pre-*` hooks block — failure aborts the operation (except `pre-start`, which warns and continues). `post-*` hooks run in the background with output logged to `.git/wt/logs/{branch}-{source}-{hook}-{name}.log`. Use `-v` to see expanded command details for background hooks. +`pre-*` hooks block — failure aborts the operation. `post-*` hooks run in the background with output logged to `.git/wt/logs/{branch}-{source}-{hook}-{name}.log`. Use `-v` to see expanded command details for background hooks. The most common starting point is `post-start` — it runs background tasks (dev servers, file copying, builds) when creating a worktree. diff --git a/src/commands/worktree/hooks.rs b/src/commands/worktree/hooks.rs index 1990bcdb4c..298d89404c 100644 --- a/src/commands/worktree/hooks.rs +++ b/src/commands/worktree/hooks.rs @@ -25,7 +25,7 @@ impl<'a> CommandContext<'a> { self, HookType::PreStart, extra_vars, - HookFailureStrategy::Warn, + HookFailureStrategy::FailFast, None, crate::output::post_hook_display_path(self.worktree_path), ) diff --git a/tests/integration_tests/post_start_commands.rs b/tests/integration_tests/post_start_commands.rs index 143488fd76..3ae7f344bc 100644 --- a/tests/integration_tests/post_start_commands.rs +++ b/tests/integration_tests/post_start_commands.rs @@ -108,7 +108,7 @@ approved-commands = ["exit 1"] "#, ); - // Should show warning but continue (worktree should still be created) + // Failing pre-start hook (via deprecated post-create name) aborts with FailFast snapshot_switch( "post_create_failing_command", &repo, diff --git a/tests/integration_tests/user_hooks.rs b/tests/integration_tests/user_hooks.rs index ef369d72dc..b6620570be 100644 --- a/tests/integration_tests/user_hooks.rs +++ b/tests/integration_tests/user_hooks.rs @@ -165,14 +165,16 @@ failing = "exit 1" "#, ); - // Failing user hook should produce warning but not block creation + // Failing pre-start hook (via deprecated post-create name) aborts with FailFast. + // The worktree is already created before pre-start runs (it was renamed from + // post-create), so the worktree exists but the command exits non-zero. snapshot_switch("user_post_create_failure", &repo, &["--create", "feature"]); - // Worktree should still be created despite hook failure + // Worktree exists (created before pre-start ran) but the command failed let worktree_path = repo.root_path().parent().unwrap().join("repo.feature"); assert!( worktree_path.exists(), - "Worktree should be created even if post-create hook fails" + "Worktree should exist — it was created before pre-start ran" ); } @@ -1125,6 +1127,24 @@ fn test_standalone_hook_post_create(repo: TestRepo) { assert!(content.contains("STANDALONE_POST_CREATE")); } +#[rstest] +fn test_standalone_hook_pre_start_fails_on_failure(repo: TestRepo) { + // pre-start hooks use FailFast like all other pre-* hooks — consistent with + // the symmetric pre (blocking, fail-fast) / post (background, warn) pattern. + repo.write_project_config(r#"pre-start = "exit 1""#); + + let output = repo + .wt_command() + .args(["hook", "pre-start", "--yes"]) + .output() + .unwrap(); + + assert!( + !output.status.success(), + "wt hook pre-start should exit non-zero when the hook fails (fail-fast, like all pre-* hooks)" + ); +} + #[rstest] fn test_standalone_hook_post_start(repo: TestRepo) { // Write project config with post-start hook diff --git a/tests/snapshots/integration__integration_tests__post_start_commands__post_create_failing_command.snap b/tests/snapshots/integration__integration_tests__post_start_commands__post_create_failing_command.snap index aae955ba5f..28d1500250 100644 --- a/tests/snapshots/integration__integration_tests__post_start_commands__post_create_failing_command.snap +++ b/tests/snapshots/integration__integration_tests__post_start_commands__post_create_failing_command.snap @@ -38,16 +38,13 @@ info: WORKTRUNK_TEST_SKIP_URL_HEALTH_CHECK: "1" XDG_CONFIG_HOME: "[TEST_CONFIG_HOME]" --- -success: true -exit_code: 0 +success: false +exit_code: 1 ----- stdout ----- ----- stderr ----- ▲ Project config has deprecated settings. Run wt config show for details or wt config update to apply updates ◎ Running pre-start project hook @ _REPO_.feature   exit 1 -✗ Command failed: exit status: 1 -✓ Created branch feature from main and worktree @ _REPO_.feature -↳ To customize worktree locations, run wt config create -▲ Cannot change directory — shell integration not installed -↳ To enable automatic cd, run wt config shell install +✗ pre-start command failed: exit status: 1 +↳ To skip pre-start hooks, re-run with --no-verify diff --git a/tests/snapshots/integration__integration_tests__user_hooks__user_post_create_failure.snap b/tests/snapshots/integration__integration_tests__user_hooks__user_post_create_failure.snap index 568e37ebb5..5464611857 100644 --- a/tests/snapshots/integration__integration_tests__user_hooks__user_post_create_failure.snap +++ b/tests/snapshots/integration__integration_tests__user_hooks__user_post_create_failure.snap @@ -38,16 +38,13 @@ info: WORKTRUNK_TEST_SKIP_URL_HEALTH_CHECK: "1" XDG_CONFIG_HOME: "[TEST_CONFIG_HOME]" --- -success: true -exit_code: 0 +success: false +exit_code: 1 ----- stdout ----- ----- stderr ----- ▲ User config has deprecated settings. Run wt config show for details or wt config update to apply updates ◎ Running pre-start user:failing @ _REPO_.feature   exit 1 -✗ Command failing failed: exit status: 1 -✓ Created branch feature from main and worktree @ _REPO_.feature -↳ To customize worktree locations, run wt config create -▲ Cannot change directory — shell integration not installed -↳ To enable automatic cd, run wt config shell install +✗ pre-start command failed: failing: exit status: 1 +↳ To skip pre-start hooks, re-run with --no-verify