@@ -45,7 +45,10 @@ const (
45
45
// sshConfigOptions represents options that can be stored and read
46
46
// from the coder config in ~/.ssh/coder.
47
47
type sshConfigOptions struct {
48
- sshOptions []string
48
+ wait bool
49
+ noWait bool
50
+ userHostPrefix string
51
+ sshOptions []string
49
52
}
50
53
51
54
// addOptions expects options in the form of "option=value" or "option value".
@@ -100,10 +103,22 @@ func (o sshConfigOptions) equal(other sshConfigOptions) bool {
100
103
sort .Strings (opt1 )
101
104
opt2 := slices .Clone (other .sshOptions )
102
105
sort .Strings (opt2 )
103
- return slices .Equal (opt1 , opt2 )
106
+ if ! slices .Equal (opt1 , opt2 ) {
107
+ return false
108
+ }
109
+ return o .wait == other .wait && o .noWait == other .noWait && o .userHostPrefix == other .userHostPrefix
104
110
}
105
111
106
112
func (o sshConfigOptions ) asList () (list []string ) {
113
+ if o .wait {
114
+ list = append (list , "wait" )
115
+ }
116
+ if o .noWait {
117
+ list = append (list , "no-wait" )
118
+ }
119
+ if o .userHostPrefix != "" {
120
+ list = append (list , fmt .Sprintf ("ssh-host-prefix: %s" , o .userHostPrefix ))
121
+ }
107
122
for _ , opt := range o .sshOptions {
108
123
list = append (list , fmt .Sprintf ("ssh-option: %s" , opt ))
109
124
}
@@ -185,7 +200,6 @@ func (r *RootCmd) configSSH() *clibase.Cmd {
185
200
usePreviousOpts bool
186
201
dryRun bool
187
202
skipProxyCommand bool
188
- userHostPrefix string
189
203
)
190
204
client := new (codersdk.Client )
191
205
cmd := & clibase.Cmd {
@@ -207,6 +221,13 @@ func (r *RootCmd) configSSH() *clibase.Cmd {
207
221
r .InitClient (client ),
208
222
),
209
223
Handler : func (inv * clibase.Invocation ) error {
224
+ if sshConfigOpts .wait && sshConfigOpts .noWait {
225
+ return xerrors .Errorf ("cannot specify both --wait and --no-wait" )
226
+ }
227
+ if skipProxyCommand && (sshConfigOpts .wait || sshConfigOpts .noWait ) {
228
+ return xerrors .Errorf ("cannot specify --skip-proxy-command with --wait or --no-wait" )
229
+ }
230
+
210
231
recvWorkspaceConfigs := sshPrepareWorkspaceConfigs (inv .Context (), client )
211
232
212
233
out := inv .Stdout
@@ -295,7 +316,7 @@ func (r *RootCmd) configSSH() *clibase.Cmd {
295
316
// Selecting "no" will use the last config.
296
317
sshConfigOpts = * lastConfig
297
318
} else {
298
- changes = append (changes , "Use new SSH options" )
319
+ changes = append (changes , "Use new options" )
299
320
}
300
321
// Only print when prompts are shown.
301
322
if yes , _ := inv .ParsedFlags ().GetBool ("yes" ); ! yes {
@@ -336,9 +357,9 @@ func (r *RootCmd) configSSH() *clibase.Cmd {
336
357
coderdConfig .HostnamePrefix = "coder."
337
358
}
338
359
339
- if userHostPrefix != "" {
360
+ if sshConfigOpts . userHostPrefix != "" {
340
361
// Override with user flag.
341
- coderdConfig .HostnamePrefix = userHostPrefix
362
+ coderdConfig .HostnamePrefix = sshConfigOpts . userHostPrefix
342
363
}
343
364
344
365
// Ensure stable sorting of output.
@@ -363,13 +384,22 @@ func (r *RootCmd) configSSH() *clibase.Cmd {
363
384
}
364
385
365
386
if ! skipProxyCommand {
387
+ flags := ""
388
+ if sshConfigOpts .wait {
389
+ flags += " --wait"
390
+ } else if sshConfigOpts .noWait {
391
+ flags += " --no-wait"
392
+ }
366
393
defaultOptions = append (defaultOptions , fmt .Sprintf (
367
- "ProxyCommand %s --global-config %s ssh --stdio %s" ,
368
- escapedCoderBinary , escapedGlobalConfig , workspaceHostname ,
394
+ "ProxyCommand %s --global-config %s ssh --stdio%s %s" ,
395
+ escapedCoderBinary , escapedGlobalConfig , flags , workspaceHostname ,
369
396
))
370
397
}
371
398
372
- var configOptions sshConfigOptions
399
+ // Create a copy of the options so we can modify them.
400
+ configOptions := sshConfigOpts
401
+ configOptions .sshOptions = nil
402
+
373
403
// Add standard options.
374
404
err := configOptions .addOptions (defaultOptions ... )
375
405
if err != nil {
@@ -507,7 +537,19 @@ func (r *RootCmd) configSSH() *clibase.Cmd {
507
537
Flag : "ssh-host-prefix" ,
508
538
Env : "" ,
509
539
Description : "Override the default host prefix." ,
510
- Value : clibase .StringOf (& userHostPrefix ),
540
+ Value : clibase .StringOf (& sshConfigOpts .userHostPrefix ),
541
+ },
542
+ {
543
+ Flag : "wait" ,
544
+ Env : "CODER_CONFIGSSH_WAIT" , // Not to be mixed with CODER_SSH_WAIT.
545
+ Description : "Wait for the the startup script to finish executing. This is the default if the template has configured the agent startup script behavior as blocking. Can not be used together with --no-wait." ,
546
+ Value : clibase .BoolOf (& sshConfigOpts .wait ),
547
+ },
548
+ {
549
+ Flag : "no-wait" ,
550
+ Env : "CODER_CONFIGSSH_NO_WAIT" , // Not to be mixed with CODER_SSH_NO_WAIT.
551
+ Description : "Enter workspace immediately after the agent has connected. This is the default if the template has configured the agent startup script behavior as non-blocking. Can not be used together with --wait." ,
552
+ Value : clibase .BoolOf (& sshConfigOpts .noWait ),
511
553
},
512
554
cliui .SkipPromptOption (),
513
555
}
@@ -524,12 +566,25 @@ func sshConfigWriteSectionHeader(w io.Writer, addNewline bool, o sshConfigOption
524
566
_ , _ = fmt .Fprint (w , nl + sshStartToken + "\n " )
525
567
_ , _ = fmt .Fprint (w , sshConfigSectionHeader )
526
568
_ , _ = fmt .Fprint (w , sshConfigDocsHeader )
527
- if len (o .sshOptions ) > 0 {
569
+
570
+ var ow strings.Builder
571
+ if o .wait {
572
+ _ , _ = fmt .Fprintf (& ow , "# :%s\n " , "wait" )
573
+ }
574
+ if o .noWait {
575
+ _ , _ = fmt .Fprintf (& ow , "# :%s\n " , "no-wait" )
576
+ }
577
+ if o .userHostPrefix != "" {
578
+ _ , _ = fmt .Fprintf (& ow , "# :%s=%s\n " , "ssh-host-prefix" , o .userHostPrefix )
579
+ }
580
+ for _ , opt := range o .sshOptions {
581
+ _ , _ = fmt .Fprintf (& ow , "# :%s=%s\n " , "ssh-option" , opt )
582
+ }
583
+ if ow .Len () > 0 {
528
584
_ , _ = fmt .Fprint (w , sshConfigOptionsHeader )
529
- for _ , opt := range o .sshOptions {
530
- _ , _ = fmt .Fprintf (w , "# :%s=%s\n " , "ssh-option" , opt )
531
- }
585
+ _ , _ = fmt .Fprint (w , ow .String ())
532
586
}
587
+
533
588
_ , _ = fmt .Fprint (w , "#\n " )
534
589
}
535
590
@@ -545,6 +600,12 @@ func sshConfigParseLastOptions(r io.Reader) (o sshConfigOptions) {
545
600
line = strings .TrimPrefix (line , "# :" )
546
601
parts := strings .SplitN (line , "=" , 2 )
547
602
switch parts [0 ] {
603
+ case "wait" :
604
+ o .wait = true
605
+ case "no-wait" :
606
+ o .noWait = true
607
+ case "user-host-prefix" :
608
+ o .userHostPrefix = parts [1 ]
548
609
case "ssh-option" :
549
610
o .sshOptions = append (o .sshOptions , parts [1 ])
550
611
default :
0 commit comments