Skip to content

Commit c9bd08f

Browse files
committed
fix(gwai): avoid worktree output capture race
1 parent 8636a6f commit c9bd08f

3 files changed

Lines changed: 124 additions & 19 deletions

File tree

.github/workflows/check.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ jobs:
2020
run: bash scripts/test-rtk-rewrite-hook.sh
2121
- name: Test my-pr worktree transfer
2222
run: bash scripts/test-my-pr-worktree-move.sh
23+
- name: Test gwai-cmux worktree capture
24+
run: bash scripts/test-gwai-cmux-worktree-capture.sh
2325
- name: Test mise upgrade wrapper
2426
run: |
2527
command -v jq >/dev/null

home/dot_local/bin/executable_gwai-cmux

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -128,37 +128,29 @@ Task: ${prompt}"
128128
create_worktree() {
129129
local base_dir="$1"
130130
local branch_name="$2"
131-
local creation_output worktree_dir output_file helper_status
132-
133-
output_file=$(mktemp) || die "failed to create temporary output file"
134-
: >"$output_file"
131+
local worktree_dir helper_status
135132

136133
set +e
137-
(cd "$base_dir" && gw-create-worktree "$branch_name") \
138-
> >(tee -a "$output_file" >&2) \
139-
2> >(tee -a "$output_file" >&2)
134+
(cd "$base_dir" && gw-create-worktree "$branch_name")
140135
helper_status=$?
141136
set -e
142137

143-
creation_output=$(cat "$output_file") || {
144-
rm -f "$output_file"
145-
die "failed to read worktree creation output"
146-
}
147-
rm -f "$output_file"
148-
149138
[[ "$helper_status" -eq 0 ]] ||
150139
die "failed to create worktree for branch: $branch_name"
151140

152-
worktree_dir=$(printf '%s\n' "$creation_output" |
153-
awk -F= '$1 == "WORKTREE_PATH" { print substr($0, index($0, "=") + 1) }' |
154-
tail -1)
141+
worktree_dir=$(git -C "$base_dir" worktree list --porcelain |
142+
awk -v b="$branch_name" '
143+
/^worktree/ { wt = substr($0, index($0, " ") + 1) }
144+
$1 == "branch" && $2 == "refs/heads/" b && found == "" { found = wt }
145+
END { if (found != "") print found }
146+
')
155147

156148
[[ -n "$worktree_dir" ]] ||
157-
die "gw-create-worktree did not output WORKTREE_PATH"
149+
die "failed to resolve worktree for branch: $branch_name"
158150
[[ -d "$worktree_dir" ]] ||
159151
die "worktree path does not exist: $worktree_dir"
160152

161-
printf '%s\n' "$worktree_dir"
153+
created_worktree_dir="$worktree_dir"
162154
}
163155

164156
create_prompt_file() {
@@ -334,6 +326,7 @@ base_dir=""
334326
branch_name=""
335327
prompt_file=""
336328
workspace_ref=""
329+
created_worktree_dir=""
337330
inside_tab=0
338331
prompt_parts=()
339332

@@ -476,6 +469,7 @@ rename_current_cmux_workspace "$branch_name" "$workspace_ref"
476469
printf '[gwai-cmux] Branch: %s\n' "$branch_name"
477470
printf '[gwai-cmux] Creating worktree...\n'
478471

479-
worktree_dir=$(create_worktree "$base_dir" "$branch_name")
472+
create_worktree "$base_dir" "$branch_name"
473+
worktree_dir="$created_worktree_dir"
480474
printf '[gwai-cmux] Starting %s in %s\n' "$provider" "$worktree_dir"
481475
start_agent "$provider" "$worktree_dir" "$prompt"
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
#!/usr/bin/env bash
2+
3+
set -euo pipefail
4+
5+
repo_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)
6+
gwai_cmux_script="${GWAI_CMUX_SCRIPT:-$repo_root/home/dot_local/bin/executable_gwai-cmux}"
7+
test_dir=$(mktemp -d "${TMPDIR:-/tmp}/gwai-cmux-worktree-capture-test.XXXXXX")
8+
test_dir=$(cd "$test_dir" && pwd -P)
9+
background_pid=""
10+
11+
cleanup() {
12+
if [[ -n "$background_pid" ]] && kill -0 "$background_pid" 2>/dev/null; then
13+
kill "$background_pid"
14+
wait "$background_pid" 2>/dev/null || true
15+
fi
16+
rm -rf "$test_dir"
17+
}
18+
trap cleanup EXIT
19+
20+
fail() {
21+
printf 'FAIL: %s\n' "$*" >&2
22+
exit 1
23+
}
24+
25+
bin_dir="$test_dir/bin"
26+
base_dir="$test_dir/base"
27+
worktree_dir="$test_dir/worktree"
28+
run_output="$test_dir/run-output"
29+
cmux_log="$test_dir/cmux.log"
30+
zsh_log="$test_dir/zsh.log"
31+
background_pid_file="$test_dir/background.pid"
32+
mkdir -p "$bin_dir" "$base_dir"
33+
git init -q "$base_dir"
34+
git -C "$base_dir" config user.name 'gwai-cmux test'
35+
git -C "$base_dir" config user.email 'gwai-cmux@example.invalid'
36+
printf 'base\n' >"$base_dir/base.txt"
37+
git -C "$base_dir" add base.txt
38+
git -C "$base_dir" commit -qm 'test: add base file'
39+
40+
cat >"$bin_dir/cmux" <<'MOCK'
41+
#!/usr/bin/env bash
42+
set -euo pipefail
43+
44+
printf '%s\n' "$*" >>"$TEST_CMUX_LOG"
45+
MOCK
46+
47+
cat >"$bin_dir/gw-create-worktree" <<'MOCK'
48+
#!/usr/bin/env bash
49+
set -euo pipefail
50+
51+
sleep 5 &
52+
printf '%s\n' "$!" >"$TEST_BACKGROUND_PID_FILE"
53+
printf 'helper progress (stdout)\n'
54+
printf 'helper progress (stderr)\n' >&2
55+
git -C "$TEST_BASE_DIR" worktree add -q -b "$1" "$TEST_WORKTREE_DIR"
56+
printf 'WORKTREE_PATH=%s\n' "$TEST_WORKTREE_DIR"
57+
MOCK
58+
59+
cat >"$bin_dir/zsh" <<'MOCK'
60+
#!/usr/bin/env bash
61+
set -euo pipefail
62+
63+
printf '%s\n' "$*" >"$TEST_ZSH_LOG"
64+
MOCK
65+
66+
chmod +x "$bin_dir/cmux" "$bin_dir/gw-create-worktree" "$bin_dir/zsh"
67+
68+
started_at=$(date +%s)
69+
set +e
70+
PATH="$bin_dir:/usr/bin:/bin" \
71+
CMUX_BIN="$bin_dir/cmux" \
72+
CMUX_WORKSPACE_ID='' \
73+
TEST_BACKGROUND_PID_FILE="$background_pid_file" \
74+
TEST_BASE_DIR="$base_dir" \
75+
TEST_CMUX_LOG="$cmux_log" \
76+
TEST_WORKTREE_DIR="$worktree_dir" \
77+
TEST_ZSH_LOG="$zsh_log" \
78+
bash "$gwai_cmux_script" \
79+
--inside-tab \
80+
--provider codex \
81+
--base-dir "$base_dir" \
82+
--branch-name fix-worktree-capture \
83+
'capture race regression' >"$run_output" 2>&1
84+
run_status=$?
85+
set -e
86+
elapsed_seconds=$(($(date +%s) - started_at))
87+
88+
((run_status == 0)) || {
89+
printf '%s\n' "$(<"$run_output")" >&2
90+
fail "gwai-cmux exited with status $run_status"
91+
}
92+
((elapsed_seconds < 4)) ||
93+
fail "gwai-cmux waited for the helper's background child (${elapsed_seconds}s)"
94+
95+
[[ -s "$background_pid_file" ]] || fail "helper did not start its background child"
96+
background_pid=$(<"$background_pid_file")
97+
kill -0 "$background_pid" 2>/dev/null ||
98+
fail "helper background child was not still running"
99+
100+
grep -Fqx -- 'rename-workspace --title fix-worktree-capture' "$cmux_log" ||
101+
fail "cmux workspace rename was not requested"
102+
grep -Fq -- "cd $worktree_dir && cdx capture\\ race\\ regression" "$zsh_log" ||
103+
fail "start_agent did not receive the expected worktree path"
104+
grep -Fq -- 'helper progress (stdout)' "$run_output" ||
105+
fail "helper stdout was not visible"
106+
grep -Fq -- 'helper progress (stderr)' "$run_output" ||
107+
fail "helper stderr was not visible"
108+
109+
printf 'test-gwai-cmux-worktree-capture: OK\n'

0 commit comments

Comments
 (0)