Skip to content

Commit b6884b2

Browse files
Copilotsawka
andauthored
Add login and port override flags to wsh ssh (#3045)
`wsh ssh` already accepted `user@host[:port]`, but there was no way to override the parsed user or port from flags. This change adds `-l/--login` and `-p/--port`, with flag values taking precedence over the target string. - **CLI surface** - Adds `-l, --login` to set the remote user - Adds `-p, --port` to set the remote port - **Override behavior** - Normalizes the final SSH target before connect/block metadata updates - Reuses the existing shared SSH target parser/formatter, so overrides apply consistently to: - the connection request - the stored connection string shown in block metadata - **Coverage** - Adds focused unit tests for: - login override - port override - combined login + port override - bare host inputs - invalid target handling when overrides are requested Example: ```bash wsh ssh -l foo root@bar.com # => connects/stores foo@bar.com wsh ssh -p 2222 root@bar.com:2022 # => connects/stores root@bar.com:2222 wsh ssh -l foo -p 2200 bar.com # => connects/stores foo@bar.com:2200 ``` <!-- START COPILOT CODING AGENT TIPS --> --- 💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: sawka <2722291+sawka@users.noreply.github.com>
1 parent 1ebc9a0 commit b6884b2

File tree

2 files changed

+104
-2
lines changed

2 files changed

+104
-2
lines changed

cmd/wsh/cmd/wshcmd-ssh.go

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2025, Command Line Inc.
1+
// Copyright 2026, Command Line Inc.
22
// SPDX-License-Identifier: Apache-2.0
33

44
package cmd
@@ -7,6 +7,7 @@ import (
77
"fmt"
88

99
"github.com/spf13/cobra"
10+
"github.com/wavetermdev/waveterm/pkg/remote"
1011
"github.com/wavetermdev/waveterm/pkg/waveobj"
1112
"github.com/wavetermdev/waveterm/pkg/wconfig"
1213
"github.com/wavetermdev/waveterm/pkg/wshrpc"
@@ -15,6 +16,8 @@ import (
1516

1617
var (
1718
identityFiles []string
19+
sshLogin string
20+
sshPort string
1821
newBlock bool
1922
)
2023

@@ -28,6 +31,8 @@ var sshCmd = &cobra.Command{
2831

2932
func init() {
3033
sshCmd.Flags().StringArrayVarP(&identityFiles, "identityfile", "i", []string{}, "add an identity file for publickey authentication")
34+
sshCmd.Flags().StringVarP(&sshLogin, "login", "l", "", "set the remote login name")
35+
sshCmd.Flags().StringVarP(&sshPort, "port", "p", "", "set the remote port")
3136
sshCmd.Flags().BoolVarP(&newBlock, "new", "n", false, "create a new terminal block with this connection")
3237
rootCmd.AddCommand(sshCmd)
3338
}
@@ -38,6 +43,11 @@ func sshRun(cmd *cobra.Command, args []string) (rtnErr error) {
3843
}()
3944

4045
sshArg := args[0]
46+
var err error
47+
sshArg, err = applySSHOverrides(sshArg, sshLogin, sshPort)
48+
if err != nil {
49+
return err
50+
}
4151
blockId := RpcContext.BlockId
4252
if blockId == "" && !newBlock {
4353
return fmt.Errorf("cannot determine blockid (not in JWT)")
@@ -91,10 +101,27 @@ func sshRun(cmd *cobra.Command, args []string) (rtnErr error) {
91101
waveobj.MetaKey_CmdCwd: nil,
92102
},
93103
}
94-
err := wshclient.SetMetaCommand(RpcClient, data, nil)
104+
err = wshclient.SetMetaCommand(RpcClient, data, nil)
95105
if err != nil {
96106
return fmt.Errorf("setting connection in block: %w", err)
97107
}
98108
WriteStderr("switched connection to %q\n", sshArg)
99109
return nil
100110
}
111+
112+
func applySSHOverrides(sshArg string, login string, port string) (string, error) {
113+
if login == "" && port == "" {
114+
return sshArg, nil
115+
}
116+
opts, err := remote.ParseOpts(sshArg)
117+
if err != nil {
118+
return "", err
119+
}
120+
if login != "" {
121+
opts.SSHUser = login
122+
}
123+
if port != "" {
124+
opts.SSHPort = port
125+
}
126+
return opts.String(), nil
127+
}

cmd/wsh/cmd/wshcmd-ssh_test.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Copyright 2026, Command Line Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package cmd
5+
6+
import "testing"
7+
8+
func TestApplySSHOverrides(t *testing.T) {
9+
tests := []struct {
10+
name string
11+
sshArg string
12+
login string
13+
port string
14+
want string
15+
wantErr bool
16+
}{
17+
{
18+
name: "no overrides preserves target",
19+
sshArg: "root@bar.com:2022",
20+
want: "root@bar.com:2022",
21+
},
22+
{
23+
name: "login override replaces parsed user",
24+
sshArg: "root@bar.com",
25+
login: "foo",
26+
want: "foo@bar.com",
27+
},
28+
{
29+
name: "port override replaces parsed port",
30+
sshArg: "root@bar.com:2022",
31+
port: "2222",
32+
want: "root@bar.com:2222",
33+
},
34+
{
35+
name: "both overrides replace parsed user and port",
36+
sshArg: "root@bar.com:2022",
37+
login: "foo",
38+
port: "2200",
39+
want: "foo@bar.com:2200",
40+
},
41+
{
42+
name: "login override adds user to bare host",
43+
sshArg: "bar.com",
44+
login: "foo",
45+
want: "foo@bar.com",
46+
},
47+
{
48+
name: "port override adds port to bare host",
49+
sshArg: "bar.com",
50+
port: "2200",
51+
want: "bar.com:2200",
52+
},
53+
{
54+
name: "invalid target returns parse error when override requested",
55+
sshArg: "bad host",
56+
login: "foo",
57+
wantErr: true,
58+
},
59+
}
60+
61+
for _, tt := range tests {
62+
t.Run(tt.name, func(t *testing.T) {
63+
got, err := applySSHOverrides(tt.sshArg, tt.login, tt.port)
64+
if (err != nil) != tt.wantErr {
65+
t.Fatalf("applySSHOverrides() error = %v, wantErr %v", err, tt.wantErr)
66+
}
67+
if tt.wantErr {
68+
return
69+
}
70+
if got != tt.want {
71+
t.Fatalf("applySSHOverrides() = %q, want %q", got, tt.want)
72+
}
73+
})
74+
}
75+
}

0 commit comments

Comments
 (0)