mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat(agent): unify session env via EnvInfoer (#26099)
The agent resolved the session home directory two ways. agentssh went through usershell, while agentproc called os.UserHomeDir directly and skipped its user.Current fallback. Routing both through a single usershell.EnvInfoer makes the resolution consistent, and agentproc now gets the same fallback as the rest of the agent. The shared seam is injectable, so SSH session tests can drive environment resolution without touching real system state.
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -24,6 +25,7 @@ import (
|
||||
"github.com/coder/coder/v2/agent/agentexec"
|
||||
"github.com/coder/coder/v2/agent/agentgit"
|
||||
"github.com/coder/coder/v2/agent/agentproc"
|
||||
"github.com/coder/coder/v2/agent/usershell"
|
||||
"github.com/coder/coder/v2/coderd/httpmw/loggermw"
|
||||
"github.com/coder/coder/v2/coderd/tracing"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
@@ -136,19 +138,43 @@ func newTestAPIWithOptions(t *testing.T, updateEnv func([]string) ([]string, err
|
||||
logger := slogtest.Make(t, &slogtest.Options{
|
||||
IgnoreErrors: true,
|
||||
}).Leveled(slog.LevelDebug)
|
||||
api := agentproc.NewAPI(logger, agentexec.DefaultExecer, updateEnv, nil, workingDir)
|
||||
api := agentproc.NewAPI(logger, agentexec.DefaultExecer, nil, nil, updateEnv, workingDir)
|
||||
t.Cleanup(func() {
|
||||
_ = api.Close()
|
||||
})
|
||||
return agentchat.Middleware(api.Routes())
|
||||
}
|
||||
|
||||
// newTestAPIWithEnvInfo creates a new API with an injected EnvInfoer
|
||||
// and an optional workingDir hook.
|
||||
func newTestAPIWithEnvInfo(t *testing.T, workingDir func() string, envInfo usershell.EnvInfoer) http.Handler {
|
||||
t.Helper()
|
||||
|
||||
logger := slogtest.Make(t, &slogtest.Options{
|
||||
IgnoreErrors: true,
|
||||
}).Leveled(slog.LevelDebug)
|
||||
api := agentproc.NewAPI(logger, agentexec.DefaultExecer, nil, envInfo, nil, workingDir)
|
||||
t.Cleanup(func() {
|
||||
_ = api.Close()
|
||||
})
|
||||
return agentchat.Middleware(api.Routes())
|
||||
}
|
||||
|
||||
// homeOverrideEnvInfo is a usershell.EnvInfoer that delegates to the
|
||||
// system implementation but reports a custom home directory.
|
||||
type homeOverrideEnvInfo struct {
|
||||
usershell.SystemEnvInfo
|
||||
home string
|
||||
}
|
||||
|
||||
func (e homeOverrideEnvInfo) HomeDir() (string, error) { return e.home, nil }
|
||||
|
||||
func TestAccessLogIncludesChatID(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
sink := testutil.NewFakeSink(t)
|
||||
logger := sink.Logger()
|
||||
api := agentproc.NewAPI(logger, agentexec.DefaultExecer, nil, nil, nil)
|
||||
api := agentproc.NewAPI(logger, agentexec.DefaultExecer, nil, nil, nil, nil)
|
||||
t.Cleanup(func() {
|
||||
_ = api.Close()
|
||||
})
|
||||
@@ -403,6 +429,40 @@ func TestStartProcess(t *testing.T) {
|
||||
require.Equal(t, homeDir, proc.WorkDir)
|
||||
})
|
||||
|
||||
t.Run("DefaultWorkDirUsesInjectedEnvInfoHome", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// With no explicit or configured directory available,
|
||||
// the home fallback must come from the injected EnvInfo
|
||||
// rather than the real user home.
|
||||
homeDir := t.TempDir()
|
||||
handler := newTestAPIWithEnvInfo(t, func() string {
|
||||
return filepath.Join(t.TempDir(), "nonexistent")
|
||||
}, homeOverrideEnvInfo{home: homeDir})
|
||||
|
||||
id := startAndGetID(t, handler, workspacesdk.StartProcessRequest{
|
||||
Command: "echo ok",
|
||||
})
|
||||
|
||||
resp := waitForExit(t, handler, id)
|
||||
require.NotNil(t, resp.ExitCode)
|
||||
require.Equal(t, 0, *resp.ExitCode)
|
||||
|
||||
w := getList(t, handler)
|
||||
require.Equal(t, http.StatusOK, w.Code)
|
||||
var listResp workspacesdk.ListProcessesResponse
|
||||
require.NoError(t, json.NewDecoder(w.Body).Decode(&listResp))
|
||||
var proc *workspacesdk.ProcessInfo
|
||||
for i := range listResp.Processes {
|
||||
if listResp.Processes[i].ID == id {
|
||||
proc = &listResp.Processes[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
require.NotNil(t, proc, "process not found in list")
|
||||
require.Equal(t, homeDir, proc.WorkDir)
|
||||
})
|
||||
|
||||
t.Run("CustomEnv", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -1084,9 +1144,9 @@ func TestHandleStartProcess_ChatHeaders_EmptyWorkDir_StillNotifies(t *testing.T)
|
||||
defer unsub()
|
||||
|
||||
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
|
||||
api := agentproc.NewAPI(logger, agentexec.DefaultExecer, func(current []string) ([]string, error) {
|
||||
api := agentproc.NewAPI(logger, agentexec.DefaultExecer, pathStore, nil, func(current []string) ([]string, error) {
|
||||
return current, nil
|
||||
}, pathStore, nil)
|
||||
}, nil)
|
||||
defer api.Close()
|
||||
|
||||
routes := agentchat.Middleware(api.Routes())
|
||||
|
||||
Reference in New Issue
Block a user