Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 8a12162

Browse files
authored
feat(cli): add --env flag for coder ssh (#12991)
This allows environment variables to be set on the SSH session. Example: coder ssh myworkspace --env VAR1=val1,VAR2=val2
1 parent e17e8aa commit 8a12162

File tree

4 files changed

+85
-9
lines changed

4 files changed

+85
-9
lines changed

cli/ssh.go

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ func (r *RootCmd) ssh() *serpent.Command {
5555
noWait bool
5656
logDirPath string
5757
remoteForwards []string
58+
env []string
5859
disableAutostart bool
5960
)
6061
client := new(codersdk.Client)
@@ -144,16 +145,23 @@ func (r *RootCmd) ssh() *serpent.Command {
144145
stack := newCloserStack(ctx, logger)
145146
defer stack.close(nil)
146147

147-
if len(remoteForwards) > 0 {
148-
for _, remoteForward := range remoteForwards {
149-
isValid := validateRemoteForward(remoteForward)
150-
if !isValid {
151-
return xerrors.Errorf(`invalid format of remote-forward, expected: remote_port:local_address:local_port`)
152-
}
153-
if isValid && stdio {
154-
return xerrors.Errorf(`remote-forward can't be enabled in the stdio mode`)
155-
}
148+
for _, remoteForward := range remoteForwards {
149+
isValid := validateRemoteForward(remoteForward)
150+
if !isValid {
151+
return xerrors.Errorf(`invalid format of remote-forward, expected: remote_port:local_address:local_port`)
152+
}
153+
if isValid && stdio {
154+
return xerrors.Errorf(`remote-forward can't be enabled in the stdio mode`)
155+
}
156+
}
157+
158+
var parsedEnv [][2]string
159+
for _, e := range env {
160+
k, v, ok := strings.Cut(e, "=")
161+
if !ok {
162+
return xerrors.Errorf("invalid environment variable setting %q", e)
156163
}
164+
parsedEnv = append(parsedEnv, [2]string{k, v})
157165
}
158166

159167
workspace, workspaceAgent, err := getWorkspaceAndAgent(ctx, inv, client, !disableAutostart, inv.Args[0])
@@ -375,6 +383,12 @@ func (r *RootCmd) ssh() *serpent.Command {
375383
}()
376384
}
377385

386+
for _, kv := range parsedEnv {
387+
if err := sshSession.Setenv(kv[0], kv[1]); err != nil {
388+
return xerrors.Errorf("setenv: %w", err)
389+
}
390+
}
391+
378392
err = sshSession.RequestPty("xterm-256color", 128, 128, gossh.TerminalModes{})
379393
if err != nil {
380394
return xerrors.Errorf("request pty: %w", err)
@@ -483,6 +497,13 @@ func (r *RootCmd) ssh() *serpent.Command {
483497
FlagShorthand: "R",
484498
Value: serpent.StringArrayOf(&remoteForwards),
485499
},
500+
{
501+
Flag: "env",
502+
Description: "Set environment variable(s) for session (key1=value1,key2=value2,...).",
503+
Env: "CODER_SSH_ENV",
504+
FlagShorthand: "e",
505+
Value: serpent.StringArrayOf(&env),
506+
},
486507
sshDisableAutostartOption(serpent.BoolOf(&disableAutostart)),
487508
}
488509
return cmd

cli/ssh_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -968,6 +968,49 @@ func TestSSH(t *testing.T) {
968968
<-cmdDone
969969
})
970970

971+
t.Run("Env", func(t *testing.T) {
972+
if runtime.GOOS == "windows" {
973+
t.Skip("Test not supported on windows")
974+
}
975+
976+
t.Parallel()
977+
978+
client, workspace, agentToken := setupWorkspaceForAgent(t)
979+
_ = agenttest.New(t, client.URL, agentToken)
980+
coderdtest.AwaitWorkspaceAgents(t, client, workspace.ID)
981+
982+
inv, root := clitest.New(t,
983+
"ssh",
984+
workspace.Name,
985+
"--env",
986+
"foo=bar,baz=qux",
987+
)
988+
clitest.SetupConfig(t, client, root)
989+
990+
pty := ptytest.New(t).Attach(inv)
991+
inv.Stderr = pty.Output()
992+
993+
// Wait super long so this doesn't flake on -race test.
994+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitSuperLong)
995+
defer cancel()
996+
997+
w := clitest.StartWithWaiter(t, inv.WithContext(ctx))
998+
defer w.Wait() // We don't care about any exit error (exit code 255: SSH connection ended unexpectedly).
999+
1000+
// Since something was output, it should be safe to write input.
1001+
// This could show a prompt or "running startup scripts", so it's
1002+
// not indicative of the SSH connection being ready.
1003+
_ = pty.Peek(ctx, 1)
1004+
1005+
// Ensure the SSH connection is ready by testing the shell
1006+
// input/output.
1007+
pty.WriteLine("echo $foo $baz")
1008+
pty.ExpectMatchContext(ctx, "bar qux")
1009+
1010+
// And we're done.
1011+
pty.WriteLine("exit")
1012+
})
1013+
9711014
t.Run("RemoteForwardUnixSocket", func(t *testing.T) {
9721015
if runtime.GOOS == "windows" {
9731016
t.Skip("Test not supported on windows")

cli/testdata/coder_ssh_--help.golden

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ OPTIONS:
99
--disable-autostart bool, $CODER_SSH_DISABLE_AUTOSTART (default: false)
1010
Disable starting the workspace automatically when connecting via SSH.
1111

12+
-e, --env string-array, $CODER_SSH_ENV
13+
Set environment variable(s) for session (key1=value1,key2=value2,...).
14+
1215
-A, --forward-agent bool, $CODER_SSH_FORWARD_AGENT
1316
Specifies whether to forward the SSH agent specified in
1417
$SSH_AUTH_SOCK.

docs/cli/ssh.md

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)