Skip to content

Commit 69ecb11

Browse files
committed
fix: Start login shells on macOS and Linux
This appends `-l` to the shell command on macOS and Linux. It also adds environment variable expansion to allow for chaining from `coder_agent.env`.
1 parent 93b1425 commit 69ecb11

File tree

5 files changed

+38
-20
lines changed

5 files changed

+38
-20
lines changed

agent/agent.go

+9-2
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,11 @@ func (a *agent) createCommand(ctx context.Context, rawCommand string, env []stri
328328
command := rawCommand
329329
if len(command) == 0 {
330330
command = shell
331+
if runtime.GOOS != "windows" {
332+
// On Linux and macOS, we should start a login
333+
// shell to consume juicy environment variables!
334+
command += " -l"
335+
}
331336
}
332337

333338
// OpenSSH executes all commands with the users current shell.
@@ -348,7 +353,6 @@ func (a *agent) createCommand(ctx context.Context, rawCommand string, env []stri
348353
return nil, xerrors.Errorf("getting os executable: %w", err)
349354
}
350355
cmd.Env = append(cmd.Env, fmt.Sprintf("USER=%s", username))
351-
cmd.Env = append(cmd.Env, fmt.Sprintf(`PATH=%s%c%s`, os.Getenv("PATH"), filepath.ListSeparator, filepath.Dir(executablePath)))
352356
// Git on Windows resolves with UNIX-style paths.
353357
// If using backslashes, it's unable to find the executable.
354358
unixExecutablePath := strings.ReplaceAll(executablePath, "\\", "/")
@@ -363,7 +367,10 @@ func (a *agent) createCommand(ctx context.Context, rawCommand string, env []stri
363367
// Load environment variables passed via the agent.
364368
// These should override all variables we manually specify.
365369
for key, value := range metadata.EnvironmentVariables {
366-
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", key, value))
370+
// Expanding environment variables allows for customization
371+
// of the $PATH, among other variables. Customers can prepand
372+
// or append to the $PATH, so allowing expand is required!
373+
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", key, os.ExpandEnv(value)))
367374
}
368375

369376
// Agent-level environment variables should take over all!

agent/agent_test.go

+19-15
Original file line numberDiff line numberDiff line change
@@ -68,21 +68,6 @@ func TestAgent(t *testing.T) {
6868
require.True(t, strings.HasSuffix(strings.TrimSpace(string(output)), "gitssh --"))
6969
})
7070

71-
t.Run("PATHHasCoder", func(t *testing.T) {
72-
t.Parallel()
73-
session := setupSSHSession(t, agent.Metadata{})
74-
command := "sh -c 'echo $PATH'"
75-
if runtime.GOOS == "windows" {
76-
command = "cmd.exe /c echo %PATH%"
77-
}
78-
output, err := session.Output(command)
79-
require.NoError(t, err)
80-
ex, err := os.Executable()
81-
t.Log(ex)
82-
require.NoError(t, err)
83-
require.True(t, strings.Contains(strings.TrimSpace(string(output)), filepath.Dir(ex)))
84-
})
85-
8671
t.Run("SessionTTY", func(t *testing.T) {
8772
t.Parallel()
8873
if runtime.GOOS == "windows" {
@@ -182,6 +167,25 @@ func TestAgent(t *testing.T) {
182167
require.Equal(t, value, strings.TrimSpace(string(output)))
183168
})
184169

170+
t.Run("EnvironmentVariableExpansion", func(t *testing.T) {
171+
t.Parallel()
172+
key := "EXAMPLE"
173+
value := "$SOMETHINGNOTSET"
174+
session := setupSSHSession(t, agent.Metadata{
175+
EnvironmentVariables: map[string]string{
176+
key: value,
177+
},
178+
})
179+
command := "sh -c 'echo $" + key + "'"
180+
if runtime.GOOS == "windows" {
181+
command = "cmd.exe /c echo %" + key + "%"
182+
}
183+
output, err := session.Output(command)
184+
require.NoError(t, err)
185+
// Output should be empty, because the variable is not set!
186+
require.Equal(t, "", strings.TrimSpace(string(output)))
187+
})
188+
185189
t.Run("StartupScript", func(t *testing.T) {
186190
t.Parallel()
187191
tempPath := filepath.Join(os.TempDir(), "content.txt")

cli/agent.go

+10
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package cli
22

33
import (
44
"context"
5+
"fmt"
56
"net/http"
67
_ "net/http/pprof" //nolint: gosec
78
"net/url"
@@ -138,6 +139,15 @@ func workspaceAgent() *cobra.Command {
138139
}
139140
}
140141

142+
executablePath, err := os.Executable()
143+
if err != nil {
144+
return xerrors.Errorf("getting os executable: %w", err)
145+
}
146+
err = os.Setenv("PATH", fmt.Sprintf("%s%c%s", os.Getenv("PATH"), filepath.ListSeparator, filepath.Dir(executablePath)))
147+
if err != nil {
148+
return xerrors.Errorf("add executable to $PATH: %w", err)
149+
}
150+
141151
closer := agent.New(client.ListenWorkspaceAgent, &agent.Options{
142152
Logger: logger,
143153
EnvironmentVariables: map[string]string{

go.mod

-2
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,6 @@ require github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
134134

135135
require (
136136
github.com/agnivade/levenshtein v1.0.1 // indirect
137-
github.com/stretchr/objx v0.2.0 // indirect
138137
github.com/vektah/gqlparser/v2 v2.4.4 // indirect
139138
github.com/yuin/goldmark v1.4.12 // indirect
140139
)
@@ -234,7 +233,6 @@ require (
234233
github.com/spf13/jwalterweatherman v1.1.0 // indirect
235234
github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af // indirect
236235
github.com/tdewolff/parse/v2 v2.6.0 // indirect
237-
github.com/vektah/gqlparser/v2 v2.4.4 // indirect
238236
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
239237
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
240238
github.com/xeipuuv/gojsonschema v1.2.0 // indirect

go.sum

-1
Original file line numberDiff line numberDiff line change
@@ -1510,7 +1510,6 @@ github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag
15101510
github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
15111511
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
15121512
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
1513-
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
15141513
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
15151514
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
15161515
github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=

0 commit comments

Comments
 (0)