|
4 | 4 | type Workspace, |
5 | 5 | type WorkspaceAgent, |
6 | 6 | } from "coder/site/src/api/typesGenerated"; |
7 | | -import * as jsonc from "jsonc-parser"; |
8 | 7 | import * as fs from "node:fs/promises"; |
9 | 8 | import * as os from "node:os"; |
10 | 9 | import * as path from "node:path"; |
@@ -60,6 +59,7 @@ import { |
60 | 59 | } from "./sshConfig"; |
61 | 60 | import { SshProcessMonitor } from "./sshProcess"; |
62 | 61 | import { computeSSHProperties, sshSupportsSetEnv } from "./sshSupport"; |
| 62 | +import { type SettingOverride, applySettingOverrides } from "./userSettings"; |
63 | 63 | import { WorkspaceStateMachine } from "./workspaceStateMachine"; |
64 | 64 |
|
65 | 65 | export interface RemoteDetails extends vscode.Disposable { |
@@ -474,86 +474,56 @@ export class Remote { |
474 | 474 | const inbox = await Inbox.create(workspace, workspaceClient, this.logger); |
475 | 475 | disposables.push(inbox); |
476 | 476 |
|
477 | | - // Do some janky setting manipulation. |
478 | 477 | this.logger.info("Modifying settings..."); |
479 | 478 | const remotePlatforms = vscodeProposed.workspace |
480 | 479 | .getConfiguration() |
481 | 480 | .get<Record<string, string>>("remote.SSH.remotePlatform", {}); |
482 | 481 | const connTimeout = vscodeProposed.workspace |
483 | 482 | .getConfiguration() |
484 | 483 | .get<number | undefined>("remote.SSH.connectTimeout"); |
| 484 | + const reconnGraceTime = vscodeProposed.workspace |
| 485 | + .getConfiguration() |
| 486 | + .get<number | undefined>("remote.SSH.reconnectionGraceTime"); |
485 | 487 |
|
486 | | - // We have to directly munge the settings file with jsonc because trying to |
487 | | - // update properly through the extension API hangs indefinitely. Possibly |
488 | | - // VS Code is trying to update configuration on the remote, which cannot |
489 | | - // connect until we finish here leading to a deadlock. We need to update it |
490 | | - // locally, anyway, and it does not seem possible to force that via API. |
491 | | - let settingsContent = "{}"; |
492 | | - try { |
493 | | - settingsContent = await fs.readFile( |
494 | | - this.pathResolver.getUserSettingsPath(), |
495 | | - "utf8", |
496 | | - ); |
497 | | - } catch { |
498 | | - // Ignore! It's probably because the file doesn't exist. |
499 | | - } |
| 488 | + const overrides: SettingOverride[] = []; |
500 | 489 |
|
501 | | - // Add the remote platform for this host to bypass a step where VS Code asks |
502 | | - // the user for the platform. |
503 | | - let mungedPlatforms = false; |
504 | | - if ( |
505 | | - !remotePlatforms[parts.sshHost] || |
506 | | - remotePlatforms[parts.sshHost] !== agent.operating_system |
507 | | - ) { |
508 | | - remotePlatforms[parts.sshHost] = agent.operating_system; |
509 | | - settingsContent = jsonc.applyEdits( |
510 | | - settingsContent, |
511 | | - jsonc.modify( |
512 | | - settingsContent, |
513 | | - ["remote.SSH.remotePlatform"], |
514 | | - remotePlatforms, |
515 | | - {}, |
516 | | - ), |
517 | | - ); |
518 | | - mungedPlatforms = true; |
| 490 | + // Bypass the platform prompt by setting the remote platform for this host. |
| 491 | + if (remotePlatforms[parts.sshHost] !== agent.operating_system) { |
| 492 | + overrides.push({ |
| 493 | + key: "remote.SSH.remotePlatform", |
| 494 | + value: { |
| 495 | + ...remotePlatforms, |
| 496 | + [parts.sshHost]: agent.operating_system, |
| 497 | + }, |
| 498 | + }); |
519 | 499 | } |
520 | 500 |
|
521 | | - // VS Code ignores the connect timeout in the SSH config and uses a default |
522 | | - // of 15 seconds, which can be too short in the case where we wait for |
523 | | - // startup scripts. For now we hardcode a longer value. Because this is |
524 | | - // potentially overwriting user configuration, it feels a bit sketchy. If |
525 | | - // microsoft/vscode-remote-release#8519 is resolved we can remove this. |
| 501 | + // VS Code's default connect timeout of 15s is too short when waiting for |
| 502 | + // startup scripts. Enforce a minimum. |
526 | 503 | const minConnTimeout = 1800; |
527 | | - let mungedConnTimeout = false; |
528 | 504 | if (!connTimeout || connTimeout < minConnTimeout) { |
529 | | - settingsContent = jsonc.applyEdits( |
530 | | - settingsContent, |
531 | | - jsonc.modify( |
532 | | - settingsContent, |
533 | | - ["remote.SSH.connectTimeout"], |
534 | | - minConnTimeout, |
535 | | - {}, |
536 | | - ), |
537 | | - ); |
538 | | - mungedConnTimeout = true; |
| 505 | + overrides.push({ |
| 506 | + key: "remote.SSH.connectTimeout", |
| 507 | + value: minConnTimeout, |
| 508 | + }); |
539 | 509 | } |
540 | 510 |
|
541 | | - if (mungedPlatforms || mungedConnTimeout) { |
542 | | - try { |
543 | | - await fs.writeFile( |
544 | | - this.pathResolver.getUserSettingsPath(), |
545 | | - settingsContent, |
546 | | - ); |
547 | | - } catch (ex) { |
548 | | - // This could be because the user's settings.json is read-only. This is |
549 | | - // the case when using home-manager on NixOS, for example. Failure to |
550 | | - // write here is not necessarily catastrophic since the user will be |
551 | | - // asked for the platform and the default timeout might be sufficient. |
552 | | - mungedPlatforms = mungedConnTimeout = false; |
553 | | - this.logger.warn("Failed to configure settings", ex); |
554 | | - } |
| 511 | + // VS Code's default reconnection grace time (ProtocolConstants.ReconnectionGraceTime) |
| 512 | + // is 3 hours (10800s). Coder workspaces commonly go offline overnight, so we |
| 513 | + // bump to 8 hours. See https://github.com/microsoft/vscode/blob/main/src/vs/base/parts/ipc/common/ipc.net.ts |
| 514 | + if (reconnGraceTime === undefined) { |
| 515 | + overrides.push({ |
| 516 | + key: "remote.SSH.reconnectionGraceTime", |
| 517 | + value: 28800, // 8 hours in seconds |
| 518 | + }); |
555 | 519 | } |
556 | 520 |
|
| 521 | + await applySettingOverrides( |
| 522 | + this.pathResolver.getUserSettingsPath(), |
| 523 | + overrides, |
| 524 | + this.logger, |
| 525 | + ); |
| 526 | + |
557 | 527 | const logDir = this.getLogDir(featureSet); |
558 | 528 |
|
559 | 529 | // This ensures the Remote SSH extension resolves the host to execute the |
|
0 commit comments