@@ -54,6 +54,7 @@ type sshConfigOptions struct {
54
54
disableAutostart bool
55
55
header []string
56
56
headerCommand string
57
+ removedKeys map [string ]bool
57
58
}
58
59
59
60
// addOptions expects options in the form of "option=value" or "option value".
@@ -74,30 +75,20 @@ func (o *sshConfigOptions) addOption(option string) error {
74
75
if err != nil {
75
76
return err
76
77
}
77
- for i ,existing := range o .sshOptions {
78
- // Override existing option if they share the same key.
79
- // This is case-insensitive. Parsing each time might be a little slow,
80
- // but it is ok.
81
- existingKey ,_ ,err := codersdk .ParseSSHConfigOption (existing )
82
- if err != nil {
83
- // Don't mess with original values if there is an error.
84
- // This could have come from the user's manual edits.
85
- continue
86
- }
87
- if strings .EqualFold (existingKey ,key ) {
88
- if value == "" {
89
- // Delete existing option.
90
- o .sshOptions = append (o .sshOptions [:i ],o .sshOptions [i + 1 :]... )
91
- }else {
92
- // Override existing option.
93
- o .sshOptions [i ]= option
94
- }
95
- return nil
96
- }
78
+ lowerKey := strings .ToLower (key )
79
+ if o .removedKeys != nil && o .removedKeys [lowerKey ] {
80
+ // Key marked as removed, skip.
81
+ return nil
97
82
}
98
- // Only append the option if it is not empty.
83
+ // Only append the option if it is not empty
84
+ // (we interpret empty as removal).
99
85
if value != "" {
100
86
o .sshOptions = append (o .sshOptions ,option )
87
+ }else {
88
+ if o .removedKeys == nil {
89
+ o .removedKeys = make (map [string ]bool )
90
+ }
91
+ o .removedKeys [lowerKey ]= true
101
92
}
102
93
return nil
103
94
}
@@ -245,6 +236,8 @@ func (r *RootCmd) configSSH() *serpent.Command {
245
236
r .InitClient (client ),
246
237
),
247
238
Handler :func (inv * serpent.Invocation )error {
239
+ ctx := inv .Context ()
240
+
248
241
if sshConfigOpts .waitEnum != "auto" && skipProxyCommand {
249
242
// The wait option is applied to the ProxyCommand. If the user
250
243
// specifies skip-proxy-command, then wait cannot be applied.
@@ -253,7 +246,14 @@ func (r *RootCmd) configSSH() *serpent.Command {
253
246
sshConfigOpts .header = r .header
254
247
sshConfigOpts .headerCommand = r .headerCommand
255
248
256
- recvWorkspaceConfigs := sshPrepareWorkspaceConfigs (inv .Context (),client )
249
+ // Talk to the API early to prevent the version mismatch
250
+ // warning from being printed in the middle of a prompt.
251
+ // This is needed because the asynchronous requests issued
252
+ // by sshPrepareWorkspaceConfigs may otherwise trigger the
253
+ // warning at any time.
254
+ _ ,_ = client .BuildInfo (ctx )
255
+
256
+ recvWorkspaceConfigs := sshPrepareWorkspaceConfigs (ctx ,client )
257
257
258
258
out := inv .Stdout
259
259
if dryRun {
@@ -375,7 +375,7 @@ func (r *RootCmd) configSSH() *serpent.Command {
375
375
return xerrors .Errorf ("fetch workspace configs failed: %w" ,err )
376
376
}
377
377
378
- coderdConfig ,err := client .SSHConfiguration (inv . Context () )
378
+ coderdConfig ,err := client .SSHConfiguration (ctx )
379
379
if err != nil {
380
380
// If the error is 404, this deployment does not support
381
381
// this endpoint yet. Do not error, just assume defaults.
@@ -440,26 +440,29 @@ func (r *RootCmd) configSSH() *serpent.Command {
440
440
configOptions := sshConfigOpts
441
441
configOptions .sshOptions = nil
442
442
443
- // Add standard options.
444
- err := configOptions .addOptions (defaultOptions ... )
445
- if err != nil {
446
- return err
443
+ // User options first (SSH only uses the first
444
+ // option unless it can be given multiple times)
445
+ for _ ,opt := range sshConfigOpts .sshOptions {
446
+ err := configOptions .addOptions (opt )
447
+ if err != nil {
448
+ return xerrors .Errorf ("add flag config option %q: %w" ,opt ,err )
449
+ }
447
450
}
448
451
449
- // Override with deployment options
452
+ // Deployment options second, allow them to
453
+ // override standard options.
450
454
for k ,v := range coderdConfig .SSHConfigOptions {
451
455
opt := fmt .Sprintf ("%s %s" ,k ,v )
452
456
err := configOptions .addOptions (opt )
453
457
if err != nil {
454
458
return xerrors .Errorf ("add coderd config option %q: %w" ,opt ,err )
455
459
}
456
460
}
457
- // Override with flag options
458
- for _ ,opt := range sshConfigOpts .sshOptions {
459
- err := configOptions .addOptions (opt )
460
- if err != nil {
461
- return xerrors .Errorf ("add flag config option %q: %w" ,opt ,err )
462
- }
461
+
462
+ // Finally, add the standard options.
463
+ err := configOptions .addOptions (defaultOptions ... )
464
+ if err != nil {
465
+ return err
463
466
}
464
467
465
468
hostBlock := []string {