@@ -265,43 +265,62 @@ export class Remote {
265
265
agent = matchingAgents [ 0 ]
266
266
}
267
267
268
- let remotePlatforms = this . vscodeProposed . workspace
268
+ const hostname = authorityParts [ 1 ]
269
+ const remotePlatforms = this . vscodeProposed . workspace
269
270
. getConfiguration ( )
270
- . get < Record < string , string > > ( "remote.SSH.remotePlatform" )
271
- remotePlatforms = {
272
- ...remotePlatforms ,
273
- [ `${ authorityParts [ 1 ] } ` ] : agent . operating_system ,
274
- }
275
-
276
- // VS Code ignores the connect timeout in the SSH config and uses a default
277
- // of 15 seconds, which can be too short in the case where we wait for
278
- // startup scripts. For now we hardcode a longer value.
279
- // If microsoft/vscode-remote-release#8519 is resolved we can remove this.
280
- const connectTimeout = 1800
281
-
271
+ . get < Record < string , string > > ( "remote.SSH.remotePlatform" , { } )
272
+ const connTimeout = this . vscodeProposed . workspace . getConfiguration ( ) . get < number > ( "remote.SSH.connectTimeout" )
273
+
274
+ // We have to directly munge the settings file with jsonc because trying to
275
+ // update properly through the extension API hangs indefinitely. Possibly
276
+ // VS Code is trying to update configuration on the remote, which cannot
277
+ // connect until we finish here leading to a deadlock. We need to update it
278
+ // locally, anyway, and it does not seem possible to force that via API.
282
279
let settingsContent = "{}"
283
280
try {
284
281
settingsContent = await fs . readFile ( this . storage . getUserSettingsPath ( ) , "utf8" )
285
282
} catch ( ex ) {
286
283
// Ignore! It's probably because the file doesn't exist.
287
284
}
288
285
289
- settingsContent = jsonc . applyEdits (
290
- settingsContent ,
291
- jsonc . modify ( settingsContent , [ "remote.SSH.remotePlatform" ] , remotePlatforms , { } ) ,
292
- )
286
+ // Add the remote platform for this host to bypass a step where VS Code asks
287
+ // the user for the platform.
288
+ let mungedPlatforms = false
289
+ if ( ! remotePlatforms [ hostname ] || remotePlatforms [ hostname ] !== agent . operating_system ) {
290
+ remotePlatforms [ hostname ] = agent . operating_system
291
+ settingsContent = jsonc . applyEdits (
292
+ settingsContent ,
293
+ jsonc . modify ( settingsContent , [ "remote.SSH.remotePlatform" ] , remotePlatforms , { } ) ,
294
+ )
295
+ mungedPlatforms = true
296
+ }
293
297
294
- settingsContent = jsonc . applyEdits (
295
- settingsContent ,
296
- jsonc . modify ( settingsContent , [ "remote.SSH.connectTimeout" ] , connectTimeout , { } ) ,
297
- )
298
+ // VS Code ignores the connect timeout in the SSH config and uses a default
299
+ // of 15 seconds, which can be too short in the case where we wait for
300
+ // startup scripts. For now we hardcode a longer value. Because this is
301
+ // potentially overwriting user configuration, it feels a bit sketchy. If
302
+ // microsoft/vscode-remote-release#8519 is resolved we can remove this but
303
+ // for now to mitigate the sketchiness we will reset it after connecting.
304
+ const minConnTimeout = 1800
305
+ let mungedConnTimeout = false
306
+ if ( ! connTimeout || connTimeout < minConnTimeout ) {
307
+ settingsContent = jsonc . applyEdits (
308
+ settingsContent ,
309
+ jsonc . modify ( settingsContent , [ "remote.SSH.connectTimeout" ] , minConnTimeout , { } ) ,
310
+ )
311
+ mungedConnTimeout = true
312
+ }
298
313
299
- try {
300
- await fs . writeFile ( this . storage . getUserSettingsPath ( ) , settingsContent )
301
- } catch ( ex ) {
302
- // The user will just be prompted instead, which is fine!
303
- // If a user's settings.json is read-only, then we can't write to it.
304
- // This is the case when using home-manager on NixOS.
314
+ if ( mungedPlatforms || mungedConnTimeout ) {
315
+ try {
316
+ await fs . writeFile ( this . storage . getUserSettingsPath ( ) , settingsContent )
317
+ } catch ( ex ) {
318
+ // This could be because the user's settings.json is read-only. This is
319
+ // the case when using home-manager on NixOS, for example. Failure to
320
+ // write here is not necessarily catastrophic since the user will be
321
+ // asked for the platform and the default timeout might be sufficient.
322
+ mungedPlatforms = mungedConnTimeout = false
323
+ }
305
324
}
306
325
307
326
const workspaceUpdate = new vscode . EventEmitter < Workspace > ( )
@@ -445,6 +464,26 @@ export class Remote {
445
464
await this . updateSSHConfig ( authorityParts [ 1 ] , hasCoderLogs )
446
465
447
466
this . findSSHProcessID ( ) . then ( ( pid ) => {
467
+ // Once the SSH process has spawned we can reset the timeout.
468
+ if ( mungedConnTimeout ) {
469
+ // Re-read settings in case they changed.
470
+ fs . readFile ( this . storage . getUserSettingsPath ( ) , "utf8" ) . then ( async ( rawSettings ) => {
471
+ try {
472
+ await fs . writeFile (
473
+ this . storage . getUserSettingsPath ( ) ,
474
+ jsonc . applyEdits (
475
+ rawSettings ,
476
+ jsonc . modify ( rawSettings , [ "remote.SSH.connectTimeout" ] , connTimeout , { } ) ,
477
+ ) ,
478
+ )
479
+ } catch ( error ) {
480
+ this . storage . writeToCoderOutputChannel (
481
+ `Failed to reset remote.SSH.connectTimeout back to ${ connTimeout } : ${ error } ` ,
482
+ )
483
+ }
484
+ } )
485
+ }
486
+
448
487
if ( ! pid ) {
449
488
// TODO: Show an error here!
450
489
return
0 commit comments