@@ -45,7 +45,9 @@ 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
+ waitEnum string
49
+ userHostPrefix string
50
+ sshOptions []string
49
51
}
50
52
51
53
// addOptions expects options in the form of "option=value" or "option value".
@@ -100,10 +102,19 @@ func (o sshConfigOptions) equal(other sshConfigOptions) bool {
100
102
sort .Strings (opt1 )
101
103
opt2 := slices .Clone (other .sshOptions )
102
104
sort .Strings (opt2 )
103
- return slices .Equal (opt1 , opt2 )
105
+ if ! slices .Equal (opt1 , opt2 ) {
106
+ return false
107
+ }
108
+ return o .waitEnum == other .waitEnum && o .userHostPrefix == other .userHostPrefix
104
109
}
105
110
106
111
func (o sshConfigOptions ) asList () (list []string ) {
112
+ if o .waitEnum != "auto" {
113
+ list = append (list , fmt .Sprintf ("wait: %s" , o .waitEnum ))
114
+ }
115
+ if o .userHostPrefix != "" {
116
+ list = append (list , fmt .Sprintf ("ssh-host-prefix: %s" , o .userHostPrefix ))
117
+ }
107
118
for _ , opt := range o .sshOptions {
108
119
list = append (list , fmt .Sprintf ("ssh-option: %s" , opt ))
109
120
}
@@ -178,14 +189,14 @@ func sshPrepareWorkspaceConfigs(ctx context.Context, client *codersdk.Client) (r
178
189
}
179
190
}
180
191
192
+ //nolint:gocyclo
181
193
func (r * RootCmd ) configSSH () * clibase.Cmd {
182
194
var (
183
195
sshConfigFile string
184
196
sshConfigOpts sshConfigOptions
185
197
usePreviousOpts bool
186
198
dryRun bool
187
199
skipProxyCommand bool
188
- userHostPrefix string
189
200
)
190
201
client := new (codersdk.Client )
191
202
cmd := & clibase.Cmd {
@@ -207,6 +218,10 @@ func (r *RootCmd) configSSH() *clibase.Cmd {
207
218
r .InitClient (client ),
208
219
),
209
220
Handler : func (inv * clibase.Invocation ) error {
221
+ if sshConfigOpts .waitEnum != "auto" && skipProxyCommand {
222
+ return xerrors .Errorf ("cannot specify both --skip-proxy-command and --wait" )
223
+ }
224
+
210
225
recvWorkspaceConfigs := sshPrepareWorkspaceConfigs (inv .Context (), client )
211
226
212
227
out := inv .Stdout
@@ -295,7 +310,7 @@ func (r *RootCmd) configSSH() *clibase.Cmd {
295
310
// Selecting "no" will use the last config.
296
311
sshConfigOpts = * lastConfig
297
312
} else {
298
- changes = append (changes , "Use new SSH options" )
313
+ changes = append (changes , "Use new options" )
299
314
}
300
315
// Only print when prompts are shown.
301
316
if yes , _ := inv .ParsedFlags ().GetBool ("yes" ); ! yes {
@@ -336,9 +351,9 @@ func (r *RootCmd) configSSH() *clibase.Cmd {
336
351
coderdConfig .HostnamePrefix = "coder."
337
352
}
338
353
339
- if userHostPrefix != "" {
354
+ if sshConfigOpts . userHostPrefix != "" {
340
355
// Override with user flag.
341
- coderdConfig .HostnamePrefix = userHostPrefix
356
+ coderdConfig .HostnamePrefix = sshConfigOpts . userHostPrefix
342
357
}
343
358
344
359
// Ensure stable sorting of output.
@@ -363,13 +378,20 @@ func (r *RootCmd) configSSH() *clibase.Cmd {
363
378
}
364
379
365
380
if ! skipProxyCommand {
381
+ flags := ""
382
+ if sshConfigOpts .waitEnum != "auto" {
383
+ flags += " --wait=" + sshConfigOpts .waitEnum
384
+ }
366
385
defaultOptions = append (defaultOptions , fmt .Sprintf (
367
- "ProxyCommand %s --global-config %s ssh --stdio %s" ,
368
- escapedCoderBinary , escapedGlobalConfig , workspaceHostname ,
386
+ "ProxyCommand %s --global-config %s ssh --stdio%s %s" ,
387
+ escapedCoderBinary , escapedGlobalConfig , flags , workspaceHostname ,
369
388
))
370
389
}
371
390
372
- var configOptions sshConfigOptions
391
+ // Create a copy of the options so we can modify them.
392
+ configOptions := sshConfigOpts
393
+ configOptions .sshOptions = nil
394
+
373
395
// Add standard options.
374
396
err := configOptions .addOptions (defaultOptions ... )
375
397
if err != nil {
@@ -505,9 +527,16 @@ func (r *RootCmd) configSSH() *clibase.Cmd {
505
527
},
506
528
{
507
529
Flag : "ssh-host-prefix" ,
508
- Env : "" ,
530
+ Env : "CODER_CONFIGSSH_SSH_HOST_PREFIX " ,
509
531
Description : "Override the default host prefix." ,
510
- Value : clibase .StringOf (& userHostPrefix ),
532
+ Value : clibase .StringOf (& sshConfigOpts .userHostPrefix ),
533
+ },
534
+ {
535
+ Flag : "wait" ,
536
+ Env : "CODER_CONFIGSSH_WAIT" , // Not to be mixed with CODER_SSH_WAIT.
537
+ Description : "Specifies whether or not to wait for the startup script to finish executing. Auto means that the agent startup script behavior configured in the workspace template is used." ,
538
+ Default : "auto" ,
539
+ Value : clibase .EnumOf (& sshConfigOpts .waitEnum , "yes" , "no" , "auto" ),
511
540
},
512
541
cliui .SkipPromptOption (),
513
542
}
@@ -524,12 +553,22 @@ func sshConfigWriteSectionHeader(w io.Writer, addNewline bool, o sshConfigOption
524
553
_ , _ = fmt .Fprint (w , nl + sshStartToken + "\n " )
525
554
_ , _ = fmt .Fprint (w , sshConfigSectionHeader )
526
555
_ , _ = fmt .Fprint (w , sshConfigDocsHeader )
527
- if len (o .sshOptions ) > 0 {
556
+
557
+ var ow strings.Builder
558
+ if o .waitEnum != "auto" {
559
+ _ , _ = fmt .Fprintf (& ow , "# :%s=%s\n " , "wait" , o .waitEnum )
560
+ }
561
+ if o .userHostPrefix != "" {
562
+ _ , _ = fmt .Fprintf (& ow , "# :%s=%s\n " , "ssh-host-prefix" , o .userHostPrefix )
563
+ }
564
+ for _ , opt := range o .sshOptions {
565
+ _ , _ = fmt .Fprintf (& ow , "# :%s=%s\n " , "ssh-option" , opt )
566
+ }
567
+ if ow .Len () > 0 {
528
568
_ , _ = fmt .Fprint (w , sshConfigOptionsHeader )
529
- for _ , opt := range o .sshOptions {
530
- _ , _ = fmt .Fprintf (w , "# :%s=%s\n " , "ssh-option" , opt )
531
- }
569
+ _ , _ = fmt .Fprint (w , ow .String ())
532
570
}
571
+
533
572
_ , _ = fmt .Fprint (w , "#\n " )
534
573
}
535
574
@@ -538,13 +577,20 @@ func sshConfigWriteSectionEnd(w io.Writer) {
538
577
}
539
578
540
579
func sshConfigParseLastOptions (r io.Reader ) (o sshConfigOptions ) {
580
+ // Default values.
581
+ o .waitEnum = "auto"
582
+
541
583
s := bufio .NewScanner (r )
542
584
for s .Scan () {
543
585
line := s .Text ()
544
586
if strings .HasPrefix (line , "# :" ) {
545
587
line = strings .TrimPrefix (line , "# :" )
546
588
parts := strings .SplitN (line , "=" , 2 )
547
589
switch parts [0 ] {
590
+ case "wait" :
591
+ o .waitEnum = parts [1 ]
592
+ case "ssh-host-prefix" :
593
+ o .userHostPrefix = parts [1 ]
548
594
case "ssh-option" :
549
595
o .sshOptions = append (o .sshOptions , parts [1 ])
550
596
default :
0 commit comments