From 780033bed923b25cb02f8ec31da1a7449217493d Mon Sep 17 00:00:00 2001 From: Tim Van Wassenhove Date: Mon, 10 Aug 2026 14:06:56 +0200 Subject: [PATCH 1/8] test(e2e): export both path worlds for Git Bash scenarios Scenarios on the Git Bash leg straddle two path worlds: the interpreter speaks /c/..., the native wt.exe speaks C:\.... Bash converts POSIX-looking arguments when spawning a native binary but leaves environment variables alone, so a scenario handing wt a path via the environment needs the native form while its own assertions need the POSIX one. Export every base path in both forms. Off Windows the two coincide, so a scenario written against them stays portable across all legs. Refs #115 --- e2e/README.md | 21 +++++++++++++++++++++ e2e/run.go | 18 ++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/e2e/README.md b/e2e/README.md index 0cdb1f6..4f2f721 100644 --- a/e2e/README.md +++ b/e2e/README.md @@ -71,6 +71,27 @@ scenarios: | `output_contains` | Output includes string | | `output_not_contains` | Output excludes string | +### Scenario Variables + +POSIX scenarios (bash/zsh/fish) can reference these, set up by the generator: + +| Variable | Form | Use | +|----------|------|-----| +| `$WT_BIN` | POSIX | The `wt` binary, invocable by the interpreter | +| `$TEST_DIR`, `$REPO_DIR` | POSIX | Paths to hand to `test`, `cat`, `mkdir`, … | +| `$WORKTREE_ROOT` | native | Exported; what `wt` itself reads | +| `$WORKTREE_ROOT_POSIX` | POSIX | Assert on worktree paths from the shell | +| `$TEST_DIR_NATIVE`, `$REPO_DIR_NATIVE` | native | Paths to hand to `wt` (e.g. `WT_CONFIG=`) | + +The `_NATIVE`/`_POSIX` pairs only differ under Git Bash, where the interpreter +speaks `/c/...` and the native `wt.exe` speaks `C:\...`. Bash converts +POSIX-looking *arguments* when it spawns a native binary, but not *environment +variables* — so anything passed to `wt` through the environment needs the +native form. Everywhere else both forms are the same string, which is what +keeps a scenario using them portable. + +These are POSIX-generator only; the PowerShell generator does not define them. + ### Skip Conditions ```yaml diff --git a/e2e/run.go b/e2e/run.go index b3469ae..83395f0 100644 --- a/e2e/run.go +++ b/e2e/run.go @@ -444,11 +444,24 @@ func generatePosixScript(wtBinary, shell string, scenario Scenario, verbose, sho sb.WriteString("TEST_DIR=$(mktemp -d)\n") sb.WriteString("REPO_DIR=\"$TEST_DIR/test-repo\"\n") sb.WriteString("REPO_NAME=\"test-repo\"\n") + // A scenario running under Git Bash straddles two path worlds: the + // interpreter and its coreutils understand POSIX paths (/c/...), the native + // wt.exe understands Windows ones (C:\...). Every base path is therefore + // available in both forms — $..._NATIVE to hand to wt (as an argument value + // bash won't convert, e.g. an env var), $..._POSIX to assert on with + // test/grep. On every other OS the two forms are the same string, so a + // scenario written with them stays portable. if runtime.GOOS == "windows" { sb.WriteString("mkdir -p \"$TEST_DIR/worktrees\"\n") sb.WriteString("export WORKTREE_ROOT=$(cygpath -w \"$TEST_DIR/worktrees\")\n") + sb.WriteString("WORKTREE_ROOT_POSIX=\"$TEST_DIR/worktrees\"\n") + sb.WriteString("TEST_DIR_NATIVE=$(cygpath -w \"$TEST_DIR\")\n") + sb.WriteString("REPO_DIR_NATIVE=$(cygpath -w \"$REPO_DIR\")\n") } else { sb.WriteString("export WORKTREE_ROOT=\"$TEST_DIR/worktrees\"\n") + sb.WriteString("WORKTREE_ROOT_POSIX=\"$WORKTREE_ROOT\"\n") + sb.WriteString("TEST_DIR_NATIVE=\"$TEST_DIR\"\n") + sb.WriteString("REPO_DIR_NATIVE=\"$REPO_DIR\"\n") } sb.WriteString("mkdir -p \"$REPO_DIR\"\n") sb.WriteString("cd \"$REPO_DIR\"\n") @@ -611,6 +624,11 @@ func generateFishScript(wtBinary string, scenario Scenario, verbose, showOutput, sb.WriteString("set REPO_DIR \"$TEST_DIR/test-repo\"\n") sb.WriteString("set REPO_NAME \"test-repo\"\n") sb.WriteString("set -x WORKTREE_ROOT \"$TEST_DIR/worktrees\"\n") + // Fish never runs on Windows, so the native and POSIX forms coincide. + // They exist here only so scenarios can name them unconditionally. + sb.WriteString("set WORKTREE_ROOT_POSIX \"$WORKTREE_ROOT\"\n") + sb.WriteString("set TEST_DIR_NATIVE \"$TEST_DIR\"\n") + sb.WriteString("set REPO_DIR_NATIVE \"$REPO_DIR\"\n") sb.WriteString("mkdir -p \"$REPO_DIR\"; or exit 1\n") sb.WriteString("cd \"$REPO_DIR\"; or exit 1\n") sb.WriteString("git init --quiet; or exit 1\n") From 2d31884f0985f55b961cb3448a0ad8d181657c9f Mon Sep 17 00:00:00 2001 From: Tim Van Wassenhove Date: Mon, 10 Aug 2026 14:08:10 +0200 Subject: [PATCH 2/8] test(e2e): run the init HOME scenarios under Git Bash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit skip_os: [windows] here was standing in for a shell constraint, not an OS one: the steps use bash-only inline env syntax, which fish and PowerShell cannot parse but Git Bash can. skip_shells already excluded those three. wt resolves the home directory from %USERPROFILE% on Windows and $HOME elsewhere, so set both — the Windows one in native form. Refs #115 --- e2e/scenarios/init.yaml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/e2e/scenarios/init.yaml b/e2e/scenarios/init.yaml index 98755a1..d342e8c 100644 --- a/e2e/scenarios/init.yaml +++ b/e2e/scenarios/init.yaml @@ -63,13 +63,16 @@ scenarios: - name: init_creates_config description: Init creates shell config with markers skip_shellenv: true - # steps use bash-only inline env syntax (HOME="..." cmd), so fish can't run them + # These four scenarios redirect wt's home lookup at TEST_DIR so they never + # touch the real ~/.bashrc. That needs bash-only inline env syntax + # (HOME="..." cmd), which rules out fish and the PowerShell shells — a + # shell constraint, not an OS one, so they do run under Git Bash. wt reads + # $HOME on Unix and %USERPROFILE% on Windows, hence both are set. skip_shells: [powershell, pwsh, fish] - skip_os: [windows] steps: - run: | touch "$TEST_DIR/.bashrc" - HOME="$TEST_DIR" $WT_BIN init bash --no-prompt + HOME="$TEST_DIR" USERPROFILE="$TEST_DIR_NATIVE" $WT_BIN init bash --no-prompt cat "$TEST_DIR/.bashrc" expect: exit_code: 0 @@ -79,12 +82,11 @@ scenarios: description: Running init twice does not duplicate config skip_shellenv: true skip_shells: [powershell, pwsh, fish] - skip_os: [windows] steps: - run: | touch "$TEST_DIR/.bashrc" - HOME="$TEST_DIR" $WT_BIN init bash --no-prompt - HOME="$TEST_DIR" $WT_BIN init bash --no-prompt + HOME="$TEST_DIR" USERPROFILE="$TEST_DIR_NATIVE" $WT_BIN init bash --no-prompt + HOME="$TEST_DIR" USERPROFILE="$TEST_DIR_NATIVE" $WT_BIN init bash --no-prompt grep -c "wt initialize >>>" "$TEST_DIR/.bashrc" expect: exit_code: 0 @@ -94,12 +96,11 @@ scenarios: description: Init --uninstall removes the config skip_shellenv: true skip_shells: [powershell, pwsh, fish] - skip_os: [windows] steps: - run: | touch "$TEST_DIR/.bashrc" - HOME="$TEST_DIR" $WT_BIN init bash --no-prompt - HOME="$TEST_DIR" $WT_BIN init bash --uninstall --no-prompt + HOME="$TEST_DIR" USERPROFILE="$TEST_DIR_NATIVE" $WT_BIN init bash --no-prompt + HOME="$TEST_DIR" USERPROFILE="$TEST_DIR_NATIVE" $WT_BIN init bash --uninstall --no-prompt grep -c "wt initialize" "$TEST_DIR/.bashrc" 2>/dev/null || echo "0" expect: output_contains: "0" @@ -108,12 +109,11 @@ scenarios: description: Init preserves existing shell config content skip_shellenv: true skip_shells: [powershell, pwsh, fish] - skip_os: [windows] steps: - run: | echo "# My existing config" > "$TEST_DIR/.bashrc" echo "export MY_VAR=hello" >> "$TEST_DIR/.bashrc" - HOME="$TEST_DIR" $WT_BIN init bash --no-prompt + HOME="$TEST_DIR" USERPROFILE="$TEST_DIR_NATIVE" $WT_BIN init bash --no-prompt cat "$TEST_DIR/.bashrc" expect: exit_code: 0 From e590e74e5b35a670d8dd13a85cdcb6cf70deaa48 Mon Sep 17 00:00:00 2001 From: Tim Van Wassenhove Date: Mon, 10 Aug 2026 14:09:26 +0200 Subject: [PATCH 3/8] test(e2e): run separator_empty_removes_slashes under Git Bash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The skip reason was already recorded as a PowerShell limitation — an empty env var reads as unset there — so express it as skip_shells and let the Git Bash leg, which has no such limitation, run it. Refs #115 --- e2e/scenarios/separator.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/e2e/scenarios/separator.yaml b/e2e/scenarios/separator.yaml index 76bf896..3d8f91e 100644 --- a/e2e/scenarios/separator.yaml +++ b/e2e/scenarios/separator.yaml @@ -59,7 +59,9 @@ scenarios: - name: separator_empty_removes_slashes description: Empty separator removes slashes entirely - skip_os: [windows] # PowerShell treats empty env vars as unset + # PowerShell treats an empty env var as unset, so it cannot express the + # case under test. Git Bash can, so this is a shell skip, not an OS one. + skip_shells: [powershell, pwsh] steps: - run: wt create feature/empty env: From 9ec87603a335036cfeeae0e8fa2321944f080731 Mon Sep 17 00:00:00 2001 From: Tim Van Wassenhove Date: Mon, 10 Aug 2026 14:10:44 +0200 Subject: [PATCH 4/8] test(e2e): run cleanup_cleans_directory under Git Bash `test -d` is a POSIX-shell constraint rather than an OS one, so swap the marker for skip_shells. The assertion reads the worktree root back from the shell, which needs the POSIX form of the path wt was given. Refs #115 --- e2e/scenarios/cleanup.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/e2e/scenarios/cleanup.yaml b/e2e/scenarios/cleanup.yaml index 756a277..6a44708 100644 --- a/e2e/scenarios/cleanup.yaml +++ b/e2e/scenarios/cleanup.yaml @@ -75,7 +75,7 @@ scenarios: - name: cleanup_cleans_directory description: Cleanup removes the worktree directory from filesystem - skip_os: [windows] # Uses Unix test -d command + skip_shells: [powershell, pwsh] # Uses Unix test -d command steps: # Create and merge a branch - run: git checkout -b cleanup-dir-branch @@ -90,6 +90,6 @@ scenarios: - run: wt cleanup --force expect: exit_code: 0 - - run: test -d "$WORKTREE_ROOT/$REPO_NAME/cleanup-dir-branch" && echo "EXISTS" || echo "REMOVED" + - run: test -d "$WORKTREE_ROOT_POSIX/$REPO_NAME/cleanup-dir-branch" && echo "EXISTS" || echo "REMOVED" expect: output_contains: REMOVED From 9500ed9a9af6fd69b5d567f4a3906d71fff91b12 Mon Sep 17 00:00:00 2001 From: Tim Van Wassenhove Date: Mon, 10 Aug 2026 14:11:56 +0200 Subject: [PATCH 5/8] test(e2e): run the two skipped remove scenarios under Git Bash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both recorded reasons — `test -d` and PowerShell exit-code handling — are shell constraints, not OS ones. Assert the removed directory through the POSIX form of the worktree root, which is what the shell can stat. Refs #115 --- e2e/scenarios/remove.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/e2e/scenarios/remove.yaml b/e2e/scenarios/remove.yaml index d2cf2f6..361d62e 100644 --- a/e2e/scenarios/remove.yaml +++ b/e2e/scenarios/remove.yaml @@ -21,7 +21,7 @@ scenarios: - name: remove_cleans_directory description: Remove deletes the worktree directory - skip_os: [windows] # Uses Unix test -d command + skip_shells: [powershell, pwsh] # Uses Unix test -d command setup: - create_branch: dir-branch steps: @@ -32,7 +32,7 @@ scenarios: - run: wt remove dir-branch expect: exit_code: 0 - - run: test -d "$WORKTREE_ROOT/$REPO_NAME/dir-branch" && echo "EXISTS" || echo "REMOVED" + - run: test -d "$WORKTREE_ROOT_POSIX/$REPO_NAME/dir-branch" && echo "EXISTS" || echo "REMOVED" expect: output_contains: REMOVED @@ -56,7 +56,7 @@ scenarios: - name: remove_nonexistent_fails description: Removing non-existent worktree fails gracefully skip_shellenv: true # Don't need shellenv wrapper for this test - skip_os: [windows] # PowerShell exit code handling differs + skip_shells: [powershell, pwsh] # PowerShell exit code handling differs steps: - run: $WT_BIN remove nonexistent-branch expect: From 0cf45ad039e06b6d9b843648306ff5a579cf4232 Mon Sep 17 00:00:00 2001 From: Tim Van Wassenhove Date: Mon, 10 Aug 2026 14:13:39 +0200 Subject: [PATCH 6/8] test(e2e): run the migrate scenarios under Git Bash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These built their own worktree root as $REPO_DIR/../worktrees and handed it to wt through the environment — the one direction bash does not convert, so under Git Bash wt would receive a POSIX path it cannot resolve. That root is the same directory the harness already exports as WORKTREE_ROOT, in wt's own path world, so use it directly and assert against its POSIX form. Refs #115 --- e2e/scenarios/migrate.yaml | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/e2e/scenarios/migrate.yaml b/e2e/scenarios/migrate.yaml index 90f56b2..3d93b6c 100644 --- a/e2e/scenarios/migrate.yaml +++ b/e2e/scenarios/migrate.yaml @@ -5,64 +5,64 @@ description: Test migration of existing worktrees to configured paths scenarios: - name: migrate_moves_worktree description: Migrate moves worktree into configured root - skip_os: [windows] # Uses Unix test -d command + skip_shells: [powershell, pwsh] # Uses Unix test -d command setup: - create_branch: migrate-apply steps: - - run: git worktree add "$REPO_DIR/../legacy/migrate-apply" migrate-apply + - run: git worktree add "$TEST_DIR/legacy/migrate-apply" migrate-apply expect: exit_code: 0 - - run: WORKTREE_ROOT="$REPO_DIR/../worktrees" wt migrate + - run: wt migrate expect: exit_code: 0 output_contains: "Moved migrate-apply" - - run: test -d "$REPO_DIR/../worktrees/test-repo/migrate-apply" && echo "TARGET_EXISTS" + - run: test -d "$WORKTREE_ROOT_POSIX/test-repo/migrate-apply" && echo "TARGET_EXISTS" expect: output_contains: TARGET_EXISTS - - run: test -d "$REPO_DIR/../legacy/migrate-apply" && echo "OLD_EXISTS" || echo "OLD_REMOVED" + - run: test -d "$TEST_DIR/legacy/migrate-apply" && echo "OLD_EXISTS" || echo "OLD_REMOVED" expect: output_contains: OLD_REMOVED - name: migrate_skip_non_empty_target_without_force description: Migrate skips when target exists and is non-empty without force - skip_os: [windows] # Uses Unix test -d command + skip_shells: [powershell, pwsh] # Uses Unix test -d command setup: - create_branch: migrate-skip steps: - - run: git worktree add "$REPO_DIR/../legacy/migrate-skip" migrate-skip + - run: git worktree add "$TEST_DIR/legacy/migrate-skip" migrate-skip expect: exit_code: 0 - - run: mkdir -p "$REPO_DIR/../worktrees/test-repo/migrate-skip" - - run: echo "conflict" > "$REPO_DIR/../worktrees/test-repo/migrate-skip/conflict.txt" - - run: WORKTREE_ROOT="$REPO_DIR/../worktrees" wt migrate + - run: mkdir -p "$WORKTREE_ROOT_POSIX/test-repo/migrate-skip" + - run: echo "conflict" > "$WORKTREE_ROOT_POSIX/test-repo/migrate-skip/conflict.txt" + - run: wt migrate expect: exit_code: 0 output_contains: "Skipped migrate-skip" - - run: test -d "$REPO_DIR/../legacy/migrate-skip" && echo "OLD_EXISTS" + - run: test -d "$TEST_DIR/legacy/migrate-skip" && echo "OLD_EXISTS" expect: output_contains: OLD_EXISTS - - run: test -f "$REPO_DIR/../worktrees/test-repo/migrate-skip/conflict.txt" && echo "CONFLICT_EXISTS" + - run: test -f "$WORKTREE_ROOT_POSIX/test-repo/migrate-skip/conflict.txt" && echo "CONFLICT_EXISTS" expect: output_contains: CONFLICT_EXISTS - name: migrate_force_overwrites_non_empty_target description: Migrate with force replaces conflicting target path - skip_os: [windows] # Uses Unix test -d command + skip_shells: [powershell, pwsh] # Uses Unix test -d command setup: - create_branch: migrate-force steps: - - run: git worktree add "$REPO_DIR/../legacy/migrate-force" migrate-force + - run: git worktree add "$TEST_DIR/legacy/migrate-force" migrate-force expect: exit_code: 0 - - run: mkdir -p "$REPO_DIR/../worktrees/test-repo/migrate-force" - - run: echo "conflict" > "$REPO_DIR/../worktrees/test-repo/migrate-force/conflict.txt" - - run: WORKTREE_ROOT="$REPO_DIR/../worktrees" wt migrate --force + - run: mkdir -p "$WORKTREE_ROOT_POSIX/test-repo/migrate-force" + - run: echo "conflict" > "$WORKTREE_ROOT_POSIX/test-repo/migrate-force/conflict.txt" + - run: wt migrate --force expect: exit_code: 0 output_contains: "Moved migrate-force" - - run: test -d "$REPO_DIR/../worktrees/test-repo/migrate-force" && echo "TARGET_EXISTS" + - run: test -d "$WORKTREE_ROOT_POSIX/test-repo/migrate-force" && echo "TARGET_EXISTS" expect: output_contains: TARGET_EXISTS - - run: test -f "$REPO_DIR/../worktrees/test-repo/migrate-force/conflict.txt" && echo "CONFLICT_EXISTS" || echo "CONFLICT_REMOVED" + - run: test -f "$WORKTREE_ROOT_POSIX/test-repo/migrate-force/conflict.txt" && echo "CONFLICT_EXISTS" || echo "CONFLICT_REMOVED" expect: output_contains: CONFLICT_REMOVED From e7147cdaa8d190fa0f6ed2fd34234c5de169ba2a Mon Sep 17 00:00:00 2001 From: Tim Van Wassenhove Date: Mon, 10 Aug 2026 14:15:09 +0200 Subject: [PATCH 7/8] test(e2e): run migrate_json_summary under Git Bash Same environment-variable crossing as the migrate scenarios: drop the hand-built WORKTREE_ROOT in favour of the one the harness exports. The remaining constraint is that the step names POSIX-generator variables, so record it as skip_shells. Refs #115 --- e2e/scenarios/format-json.yaml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/e2e/scenarios/format-json.yaml b/e2e/scenarios/format-json.yaml index ffb240f..e420c3b 100644 --- a/e2e/scenarios/format-json.yaml +++ b/e2e/scenarios/format-json.yaml @@ -65,18 +65,20 @@ scenarios: - name: migrate_json_summary description: migrate command returns structured JSON summary skip_shellenv: true - skip_os: [windows] + # $TEST_DIR is a POSIX-generator variable; the PowerShell generator names + # it $TestDir and does not translate it inside a step. + skip_shells: [powershell, pwsh] setup: - create_branch: migrate-json steps: - - run: git worktree add "$REPO_DIR/../legacy/migrate-json" migrate-json + - run: git worktree add "$TEST_DIR/legacy/migrate-json" migrate-json expect: exit_code: 0 - - run: WORKTREE_ROOT="$REPO_DIR/../worktrees" $WT_BIN --format json migrate + - run: $WT_BIN --format json migrate expect: exit_code: 0 output_contains: '"command":"wt migrate"' - - run: WORKTREE_ROOT="$REPO_DIR/../worktrees" $WT_BIN --format json migrate + - run: $WT_BIN --format json migrate expect: output_contains: '"migrated"' From 166e2a556d4d128639fb25ddcb2548b86ac89ddd Mon Sep 17 00:00:00 2001 From: Tim Van Wassenhove Date: Mon, 10 Aug 2026 14:17:05 +0200 Subject: [PATCH 8/8] test(e2e): run the hook scenarios that survive cmd /c under Git Bash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit wt spawns hook commands itself, via `cmd /c` on Windows (runHooks) — so the calling shell is irrelevant to them. Bodies referencing $WT_PATH or $WT_MAIN, or calling POSIX utilities, genuinely cannot run there; they keep skip_os but now say why. The four whose body is a bare `false` or `echo` are only held back by the POSIX syntax of the surrounding steps, so they move to skip_shells and gain the Git Bash leg. WT_CONFIG reaches wt through the environment, which bash does not path-convert, so pass it in native form. Refs #115 --- e2e/scenarios/hooks.yaml | 41 +++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/e2e/scenarios/hooks.yaml b/e2e/scenarios/hooks.yaml index ca15ebd..9ef8342 100644 --- a/e2e/scenarios/hooks.yaml +++ b/e2e/scenarios/hooks.yaml @@ -5,11 +5,14 @@ description: Test pre/post command hook functionality scenarios: - name: post_create_hook_runs description: Post-create hook executes after worktree creation + # wt runs hook commands through `cmd /c` on Windows (see runHooks), which + # neither expands $WT_PATH nor provides the POSIX utilities this hook body + # uses. That holds under Git Bash too, since it is wt that spawns the hook. skip_os: [windows] skip_shellenv: true steps: # Write a config file with a post_create hook, then create worktree - - run: mkdir -p "$TEST_DIR/config" && printf '[hooks]\npost_create = ["touch $WT_PATH/.hook-ran"]\n' > "$TEST_DIR/config/config.toml" && WT_CONFIG="$TEST_DIR/config/config.toml" $WT_BIN create hook-test + - run: mkdir -p "$TEST_DIR/config" && printf '[hooks]\npost_create = ["touch $WT_PATH/.hook-ran"]\n' > "$TEST_DIR/config/config.toml" && WT_CONFIG="$TEST_DIR_NATIVE/config/config.toml" $WT_BIN create hook-test expect: exit_code: 0 - run: test -f "$WORKTREE_ROOT/test-repo/hook-test/.hook-ran" && echo "HOOK_RAN" || echo "HOOK_MISSING" @@ -19,9 +22,9 @@ scenarios: - name: pre_create_hook_aborts description: Pre-create hook failure prevents worktree creation skip_shellenv: true - skip_os: [windows] + skip_shells: [powershell, pwsh] # steps use POSIX shell syntax steps: - - run: mkdir -p "$TEST_DIR/config" && printf '[hooks]\npre_create = ["false"]\n' > "$TEST_DIR/config/config.toml" && WT_CONFIG="$TEST_DIR/config/config.toml" $WT_BIN create blocked-branch + - run: mkdir -p "$TEST_DIR/config" && printf '[hooks]\npre_create = ["false"]\n' > "$TEST_DIR/config/config.toml" && WT_CONFIG="$TEST_DIR_NATIVE/config/config.toml" $WT_BIN create blocked-branch expect: exit_code: 1 - run: $WT_BIN list @@ -30,21 +33,24 @@ scenarios: - name: hooks_disabled_env description: WT_HOOKS_DISABLED=1 skips all hooks - skip_os: [windows] + skip_shells: [powershell, pwsh] # steps use POSIX shell syntax skip_shellenv: true steps: - - run: mkdir -p "$TEST_DIR/config" && printf '[hooks]\npre_create = ["false"]\n' > "$TEST_DIR/config/config.toml" && WT_CONFIG="$TEST_DIR/config/config.toml" WT_HOOKS_DISABLED=1 $WT_BIN create should-succeed + - run: mkdir -p "$TEST_DIR/config" && printf '[hooks]\npre_create = ["false"]\n' > "$TEST_DIR/config/config.toml" && WT_CONFIG="$TEST_DIR_NATIVE/config/config.toml" WT_HOOKS_DISABLED=1 $WT_BIN create should-succeed expect: exit_code: 0 - name: post_checkout_hook_runs description: Post-checkout hook executes after worktree checkout + # wt runs hook commands through `cmd /c` on Windows (see runHooks), which + # neither expands $WT_PATH nor provides the POSIX utilities this hook body + # uses. That holds under Git Bash too, since it is wt that spawns the hook. skip_os: [windows] skip_shellenv: true setup: - create_branch: hook-co-branch steps: - - run: mkdir -p "$TEST_DIR/config" && printf '[hooks]\npost_checkout = ["touch $WT_PATH/.checkout-hook-ran"]\n' > "$TEST_DIR/config/config.toml" && WT_CONFIG="$TEST_DIR/config/config.toml" $WT_BIN checkout hook-co-branch + - run: mkdir -p "$TEST_DIR/config" && printf '[hooks]\npost_checkout = ["touch $WT_PATH/.checkout-hook-ran"]\n' > "$TEST_DIR/config/config.toml" && WT_CONFIG="$TEST_DIR_NATIVE/config/config.toml" $WT_BIN checkout hook-co-branch expect: exit_code: 0 - run: test -f "$WORKTREE_ROOT/test-repo/hook-co-branch/.checkout-hook-ran" && echo "HOOK_RAN" || echo "HOOK_MISSING" @@ -53,6 +59,9 @@ scenarios: - name: checkout_hooks_run_on_existing_worktree description: Pre/post checkout hooks execute even when worktree already exists + # wt runs hook commands through `cmd /c` on Windows (see runHooks), which + # neither expands $WT_PATH nor provides the POSIX utilities this hook body + # uses. That holds under Git Bash too, since it is wt that spawns the hook. skip_os: [windows] skip_shellenv: true setup: @@ -64,7 +73,7 @@ scenarios: exit_code: 0 - cd: $REPO_DIR # Second checkout with hooks configured — hooks should still run - - run: mkdir -p "$TEST_DIR/config" && printf '[hooks]\npost_checkout = ["touch $WT_PATH/.existing-hook-ran"]\n' > "$TEST_DIR/config/config.toml" && WT_CONFIG="$TEST_DIR/config/config.toml" $WT_BIN checkout existing-co-branch + - run: mkdir -p "$TEST_DIR/config" && printf '[hooks]\npost_checkout = ["touch $WT_PATH/.existing-hook-ran"]\n' > "$TEST_DIR/config/config.toml" && WT_CONFIG="$TEST_DIR_NATIVE/config/config.toml" $WT_BIN checkout existing-co-branch expect: exit_code: 0 output_contains: "already exists" @@ -74,7 +83,7 @@ scenarios: - name: pre_checkout_hook_aborts_existing_worktree description: Pre-checkout hook failure prevents checkout of existing worktree - skip_os: [windows] + skip_shells: [powershell, pwsh] # steps use POSIX shell syntax skip_shellenv: true setup: - create_branch: abort-co-branch @@ -85,13 +94,13 @@ scenarios: exit_code: 0 - cd: $REPO_DIR # Second checkout with failing pre_checkout hook — should abort - - run: mkdir -p "$TEST_DIR/config" && printf '[hooks]\npre_checkout = ["false"]\n' > "$TEST_DIR/config/config.toml" && WT_CONFIG="$TEST_DIR/config/config.toml" $WT_BIN checkout abort-co-branch + - run: mkdir -p "$TEST_DIR/config" && printf '[hooks]\npre_checkout = ["false"]\n' > "$TEST_DIR/config/config.toml" && WT_CONFIG="$TEST_DIR_NATIVE/config/config.toml" $WT_BIN checkout abort-co-branch expect: exit_code: 1 - name: pre_remove_hook_runs description: Pre-remove hook executes before worktree removal - skip_os: [windows] + skip_shells: [powershell, pwsh] # steps use POSIX shell syntax skip_shellenv: true setup: - create_branch: rm-hook-branch @@ -100,13 +109,16 @@ scenarios: expect: exit_code: 0 - cd: $REPO_DIR - - run: mkdir -p "$TEST_DIR/config" && printf '[hooks]\npre_remove = ["echo HOOK_PRE_REMOVE"]\n' > "$TEST_DIR/config/config.toml" && WT_CONFIG="$TEST_DIR/config/config.toml" $WT_BIN remove rm-hook-branch + - run: mkdir -p "$TEST_DIR/config" && printf '[hooks]\npre_remove = ["echo HOOK_PRE_REMOVE"]\n' > "$TEST_DIR/config/config.toml" && WT_CONFIG="$TEST_DIR_NATIVE/config/config.toml" $WT_BIN remove rm-hook-branch expect: exit_code: 0 output_contains: "HOOK_PRE_REMOVE" - name: copy_env_file_when_exists description: Post-create hook copies .env from main to worktree when it exists + # wt runs hook commands through `cmd /c` on Windows (see runHooks), which + # neither expands $WT_PATH nor provides the POSIX utilities this hook body + # uses. That holds under Git Bash too, since it is wt that spawns the hook. skip_os: [windows] skip_shellenv: true setup: @@ -116,7 +128,7 @@ scenarios: - git_add: .env - git_commit: "add .env file" steps: - - run: mkdir -p "$TEST_DIR/config" && printf '[hooks]\npost_create = ["test -f $WT_MAIN/.env && cp $WT_MAIN/.env $WT_PATH/.env || true"]\n' > "$TEST_DIR/config/config.toml" && WT_CONFIG="$TEST_DIR/config/config.toml" $WT_BIN create env-branch + - run: mkdir -p "$TEST_DIR/config" && printf '[hooks]\npost_create = ["test -f $WT_MAIN/.env && cp $WT_MAIN/.env $WT_PATH/.env || true"]\n' > "$TEST_DIR/config/config.toml" && WT_CONFIG="$TEST_DIR_NATIVE/config/config.toml" $WT_BIN create env-branch expect: exit_code: 0 - run: cat "$WORKTREE_ROOT/test-repo/env-branch/.env" @@ -125,10 +137,13 @@ scenarios: - name: copy_env_file_skipped_when_missing description: Post-create hook silently skips when .env does not exist in main + # wt runs hook commands through `cmd /c` on Windows (see runHooks), which + # neither expands $WT_PATH nor provides the POSIX utilities this hook body + # uses. That holds under Git Bash too, since it is wt that spawns the hook. skip_os: [windows] skip_shellenv: true steps: - - run: mkdir -p "$TEST_DIR/config" && printf '[hooks]\npost_create = ["test -f $WT_MAIN/.env && cp $WT_MAIN/.env $WT_PATH/.env || true"]\n' > "$TEST_DIR/config/config.toml" && WT_CONFIG="$TEST_DIR/config/config.toml" $WT_BIN create no-env-branch + - run: mkdir -p "$TEST_DIR/config" && printf '[hooks]\npost_create = ["test -f $WT_MAIN/.env && cp $WT_MAIN/.env $WT_PATH/.env || true"]\n' > "$TEST_DIR/config/config.toml" && WT_CONFIG="$TEST_DIR_NATIVE/config/config.toml" $WT_BIN create no-env-branch expect: exit_code: 0 - run: test -f "$WORKTREE_ROOT/test-repo/no-env-branch/.env" && echo "ENV_EXISTS" || echo "ENV_MISSING"