Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions pkg/board/tmux.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"strconv"
"strings"
"sync"

"github.com/docker/docker-agent/pkg/paths"
)

// tmuxSessions manages the tmux sessions the board runs its agents in.
Expand Down Expand Up @@ -171,6 +173,16 @@ func applyServerDefaults(ctx context.Context) {
// owns sessionID and passes it via --session: the first run creates that
// session, later runs resume it.
//
// --config-dir, --data-dir, and --cache-dir forward the board's own
// directories so the agent resolves the same aliases, creates its worktree
// and session under the data dir the board watches, and shares the board's
// caches. Directory overrides are process-local flags, not inherited env,
// so without forwarding an agent spawned in an environment whose $HOME
// differs from the board's directories (e.g. a docker sandbox with the
// host dirs bind-mounted) would silently use its own empty config and a
// data dir the board never looks at. On a host with no overrides this
// forwards the defaults — a no-op.
//
// --listen exposes the run's control plane on listenSocket (a unix socket
// the board owns), so the board can observe and drive the session over HTTP
// instead of scraping the terminal.
Expand All @@ -188,8 +200,10 @@ func agentCommand(agent, sessionID, listenSocket, worktreeName, worktreeBase, pr
if err != nil {
bin = "docker-agent"
}
cmd := fmt.Sprintf("%s run %s --yolo --session %s --listen %s",
shQuote(bin), shQuote(agent), shQuote(sessionID), shQuote("unix://"+listenSocket))
cmd := fmt.Sprintf("%s run %s --yolo --config-dir %s --data-dir %s --cache-dir %s --session %s --listen %s",
shQuote(bin), shQuote(agent),
shQuote(paths.GetConfigDir()), shQuote(paths.GetDataDir()), shQuote(paths.GetCacheDir()),
shQuote(sessionID), shQuote("unix://"+listenSocket))
if worktreeName != "" {
cmd += fmt.Sprintf(" --worktree=%s --worktree-base %s", shQuote(worktreeName), shQuote(worktreeBase))
}
Expand Down
34 changes: 33 additions & 1 deletion pkg/board/tmux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"testing"

"github.com/stretchr/testify/assert"

"github.com/docker/docker-agent/pkg/paths"
)

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

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

// The board's config, data, and cache dirs are forwarded so the agent
// resolves the same aliases and creates its worktree where the board
// watches, even when the board runs with directory overrides (e.g. in a
// sandbox whose $HOME differs from the mounted host directories).
assert.Contains(t, cmd, " --config-dir "+shQuote(paths.GetConfigDir())+" ")
assert.Contains(t, cmd, " --data-dir "+shQuote(paths.GetDataDir())+" ")
assert.Contains(t, cmd, " --cache-dir "+shQuote(paths.GetCacheDir())+" ")

// Resume: no worktree flags, no prompt.
cmd = agentCommand("coder", "sess1", "/tmp/a.sock", "", "", "")
assert.NotContains(t, cmd, "--worktree")
assert.True(t, strings.HasSuffix(cmd, "--listen 'unix:///tmp/a.sock'"))
}

// TestAgentCommandForwardsDirOverrides covers the scenario the forwarding
// exists for: the board running with directory overrides. Not parallel: it
// mutates the process-global overrides, and restores them before returning
// — during the serial phase, before parallel tests resume.
func TestAgentCommandForwardsDirOverrides(t *testing.T) {
configDir, dataDir, cacheDir := t.TempDir(), t.TempDir(), t.TempDir()
paths.SetConfigDir(configDir)
paths.SetDataDir(dataDir)
paths.SetCacheDir(cacheDir)
t.Cleanup(func() {
paths.SetConfigDir("")
paths.SetDataDir("")
paths.SetCacheDir("")
})

cmd := agentCommand("coder", "sess1", "/tmp/a.sock", "", "", "")
assert.Contains(t, cmd, " --config-dir "+shQuote(configDir)+" ")
assert.Contains(t, cmd, " --data-dir "+shQuote(dataDir)+" ")
assert.Contains(t, cmd, " --cache-dir "+shQuote(cacheDir)+" ")
}

func TestTmuxFormatEscape(t *testing.T) {
t.Parallel()

Expand Down
Loading