mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix(agent): unify working directory resolution (#26102)
agentssh's CommandEnv, sftpHandler, and agentproc each resolved the session working directory on their own and had drifted: sftpHandler used the configured directory without checking it exists and bypassed the injected EnvInfoer, while the others stat-checked and fell back to home. Home and shell lookups could also skip the EnvInfoer seam through the exported usershell.HomeDir and Get. Resolve through a single usershell.ResolveWorkingDirectory and confine host home and shell lookups to usershell, so SSH sessions, the process API, and tests can no longer diverge and the injected environment is always honored. This also fixes SFTP landing in a configured directory that no longer exists. Refs coder/coder#26099
This commit is contained in:
+13
-26
@@ -14,7 +14,6 @@ import (
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strconv"
|
||||
@@ -151,6 +150,9 @@ func New(options Options) Agent {
|
||||
if options.Filesystem == nil {
|
||||
options.Filesystem = afero.NewOsFs()
|
||||
}
|
||||
if options.EnvInfo == nil {
|
||||
options.EnvInfo = &usershell.SystemEnvInfo{}
|
||||
}
|
||||
if options.TempDir == "" {
|
||||
options.TempDir = os.TempDir()
|
||||
}
|
||||
@@ -427,7 +429,7 @@ func (a *agent) init() {
|
||||
|
||||
pathStore := agentgit.NewPathStore()
|
||||
a.filesAPI = agentfiles.NewAPI(a.logger.Named("files"), a.filesystem, pathStore)
|
||||
a.processAPI = agentproc.NewAPI(a.logger.Named("processes"), a.execer, pathStore, a.envInfo, a.updateCommandEnv, func() string {
|
||||
a.processAPI = agentproc.NewAPI(a.logger.Named("processes"), a.execer, a.filesystem, pathStore, a.envInfo, a.updateCommandEnv, func() string {
|
||||
if m := a.manifest.Load(); m != nil {
|
||||
return m.Directory
|
||||
}
|
||||
@@ -1303,12 +1305,12 @@ func (a *agent) handleManifest(manifestOK *checkpoint) func(ctx context.Context,
|
||||
//
|
||||
// An example is VS Code Remote, which must know the directory
|
||||
// before initializing a connection.
|
||||
manifest.Directory, err = expandPathToAbs(manifest.Directory)
|
||||
manifest.Directory, err = a.expandPathToAbs(manifest.Directory)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("expand directory: %w", err)
|
||||
}
|
||||
// Normalize all devcontainer paths by making them absolute.
|
||||
manifest.Devcontainers = agentcontainers.ExpandAllDevcontainerPaths(a.logger, expandPathToAbs, manifest.Devcontainers)
|
||||
manifest.Devcontainers = agentcontainers.ExpandAllDevcontainerPaths(a.logger, a.expandPathToAbs, manifest.Devcontainers)
|
||||
subsys, err := agentsdk.ProtoFromSubsystems(a.subsystems)
|
||||
if err != nil {
|
||||
a.logger.Critical(ctx, "failed to convert subsystems", slog.Error(err))
|
||||
@@ -1337,7 +1339,7 @@ func (a *agent) handleManifest(manifestOK *checkpoint) func(ctx context.Context,
|
||||
// longer than file writes. Startup scripts still wait because they run
|
||||
// sequentially below. Env var injection is unaffected because it
|
||||
// happens lazily per-command in updateCommandEnv.
|
||||
homeDir, err := os.UserHomeDir()
|
||||
homeDir, err := a.envInfo.HomeDir()
|
||||
if err != nil {
|
||||
a.logger.Warn(ctx, "failed to resolve home directory for secret files", slog.Error(err))
|
||||
}
|
||||
@@ -2347,31 +2349,16 @@ lifecycleWaitLoop:
|
||||
return nil
|
||||
}
|
||||
|
||||
// userHomeDir returns the home directory of the current user, giving
|
||||
// priority to the $HOME environment variable.
|
||||
func userHomeDir() (string, error) {
|
||||
// First we check the environment.
|
||||
homedir, err := os.UserHomeDir()
|
||||
if err == nil {
|
||||
return homedir, nil
|
||||
}
|
||||
|
||||
// As a fallback, we try the user information.
|
||||
u, err := user.Current()
|
||||
if err != nil {
|
||||
return "", xerrors.Errorf("current user: %w", err)
|
||||
}
|
||||
return u.HomeDir, nil
|
||||
}
|
||||
|
||||
// expandPathToAbs converts a path to an absolute path. It primarily resolves
|
||||
// the home directory and any environment variables that may be set.
|
||||
func expandPathToAbs(path string) (string, error) {
|
||||
// the home directory and any environment variables that may be set. The home
|
||||
// directory is resolved through the agent's EnvInfoer so the injected
|
||||
// environment is honored.
|
||||
func (a *agent) expandPathToAbs(path string) (string, error) {
|
||||
if path == "" {
|
||||
return "", nil
|
||||
}
|
||||
if path[0] == '~' {
|
||||
home, err := userHomeDir()
|
||||
home, err := a.envInfo.HomeDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -2380,7 +2367,7 @@ func expandPathToAbs(path string) (string, error) {
|
||||
path = os.ExpandEnv(path)
|
||||
|
||||
if !filepath.IsAbs(path) {
|
||||
home, err := userHomeDir()
|
||||
home, err := a.envInfo.HomeDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user