Skip to content

Commit 6bfe0f0

Browse files
authored
Merge pull request #3518 from dgageot/worktree-board-ecdadd49e9747488
fix(board): forward directory overrides to spawned agents
2 parents 2a02bc4 + abd2a11 commit 6bfe0f0

2 files changed

Lines changed: 49 additions & 3 deletions

File tree

pkg/board/tmux.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import (
99
"strconv"
1010
"strings"
1111
"sync"
12+
13+
"github.com/docker/docker-agent/pkg/paths"
1214
)
1315

1416
// tmuxSessions manages the tmux sessions the board runs its agents in.
@@ -171,6 +173,16 @@ func applyServerDefaults(ctx context.Context) {
171173
// owns sessionID and passes it via --session: the first run creates that
172174
// session, later runs resume it.
173175
//
176+
// --config-dir, --data-dir, and --cache-dir forward the board's own
177+
// directories so the agent resolves the same aliases, creates its worktree
178+
// and session under the data dir the board watches, and shares the board's
179+
// caches. Directory overrides are process-local flags, not inherited env,
180+
// so without forwarding an agent spawned in an environment whose $HOME
181+
// differs from the board's directories (e.g. a docker sandbox with the
182+
// host dirs bind-mounted) would silently use its own empty config and a
183+
// data dir the board never looks at. On a host with no overrides this
184+
// forwards the defaults — a no-op.
185+
//
174186
// --listen exposes the run's control plane on listenSocket (a unix socket
175187
// the board owns), so the board can observe and drive the session over HTTP
176188
// instead of scraping the terminal.
@@ -188,8 +200,10 @@ func agentCommand(agent, sessionID, listenSocket, worktreeName, worktreeBase, pr
188200
if err != nil {
189201
bin = "docker-agent"
190202
}
191-
cmd := fmt.Sprintf("%s run %s --yolo --session %s --listen %s",
192-
shQuote(bin), shQuote(agent), shQuote(sessionID), shQuote("unix://"+listenSocket))
203+
cmd := fmt.Sprintf("%s run %s --yolo --config-dir %s --data-dir %s --cache-dir %s --session %s --listen %s",
204+
shQuote(bin), shQuote(agent),
205+
shQuote(paths.GetConfigDir()), shQuote(paths.GetDataDir()), shQuote(paths.GetCacheDir()),
206+
shQuote(sessionID), shQuote("unix://"+listenSocket))
193207
if worktreeName != "" {
194208
cmd += fmt.Sprintf(" --worktree=%s --worktree-base %s", shQuote(worktreeName), shQuote(worktreeBase))
195209
}

pkg/board/tmux_test.go

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"testing"
66

77
"github.com/stretchr/testify/assert"
8+
9+
"github.com/docker/docker-agent/pkg/paths"
810
)
911

1012
func TestShQuote(t *testing.T) {
@@ -20,16 +22,46 @@ func TestAgentCommand(t *testing.T) {
2022

2123
// First run: creates the worktree from the base.
2224
cmd := agentCommand("coder", "sess1", "/tmp/a.sock", "board-abc", "origin/main", "do the thing")
23-
assert.Contains(t, cmd, " run 'coder' --yolo --session 'sess1' --listen 'unix:///tmp/a.sock'")
25+
assert.Contains(t, cmd, " run 'coder' --yolo ")
26+
assert.Contains(t, cmd, " --session 'sess1' --listen 'unix:///tmp/a.sock'")
2427
assert.Contains(t, cmd, "--worktree='board-abc' --worktree-base 'origin/main'")
2528
assert.True(t, strings.HasSuffix(cmd, " 'do the thing'"))
2629

30+
// The board's config, data, and cache dirs are forwarded so the agent
31+
// resolves the same aliases and creates its worktree where the board
32+
// watches, even when the board runs with directory overrides (e.g. in a
33+
// sandbox whose $HOME differs from the mounted host directories).
34+
assert.Contains(t, cmd, " --config-dir "+shQuote(paths.GetConfigDir())+" ")
35+
assert.Contains(t, cmd, " --data-dir "+shQuote(paths.GetDataDir())+" ")
36+
assert.Contains(t, cmd, " --cache-dir "+shQuote(paths.GetCacheDir())+" ")
37+
2738
// Resume: no worktree flags, no prompt.
2839
cmd = agentCommand("coder", "sess1", "/tmp/a.sock", "", "", "")
2940
assert.NotContains(t, cmd, "--worktree")
3041
assert.True(t, strings.HasSuffix(cmd, "--listen 'unix:///tmp/a.sock'"))
3142
}
3243

44+
// TestAgentCommandForwardsDirOverrides covers the scenario the forwarding
45+
// exists for: the board running with directory overrides. Not parallel: it
46+
// mutates the process-global overrides, and restores them before returning
47+
// — during the serial phase, before parallel tests resume.
48+
func TestAgentCommandForwardsDirOverrides(t *testing.T) {
49+
configDir, dataDir, cacheDir := t.TempDir(), t.TempDir(), t.TempDir()
50+
paths.SetConfigDir(configDir)
51+
paths.SetDataDir(dataDir)
52+
paths.SetCacheDir(cacheDir)
53+
t.Cleanup(func() {
54+
paths.SetConfigDir("")
55+
paths.SetDataDir("")
56+
paths.SetCacheDir("")
57+
})
58+
59+
cmd := agentCommand("coder", "sess1", "/tmp/a.sock", "", "", "")
60+
assert.Contains(t, cmd, " --config-dir "+shQuote(configDir)+" ")
61+
assert.Contains(t, cmd, " --data-dir "+shQuote(dataDir)+" ")
62+
assert.Contains(t, cmd, " --cache-dir "+shQuote(cacheDir)+" ")
63+
}
64+
3365
func TestTmuxFormatEscape(t *testing.T) {
3466
t.Parallel()
3567

0 commit comments

Comments
 (0)