@@ -39,9 +39,11 @@ import {
3939import { getHeaderCommand } from "../settings/headers" ;
4040import { escapeCommandArg , expandPath } from "../util" ;
4141import {
42- AuthorityPrefix ,
4342 type AuthorityParts ,
43+ classifyRemoteAuthority ,
4444 parseRemoteAuthority ,
45+ retargetRemoteAuthority ,
46+ toCurrentAuthorityHostPrefix ,
4547} from "../util/authority" ;
4648import { createStatusBarItem } from "../util/statusBar" ;
4749import { vscodeProposed } from "../vscodeProposed" ;
@@ -77,13 +79,6 @@ import type { SecretsManager } from "../core/secretsManager";
7779import type { Logger } from "../logging/logger" ;
7880import type { LoginCoordinator } from "../login/loginCoordinator" ;
7981
80- /**
81- * Our own config, included from the user's. Keep the tilde: relative includes
82- * always resolve against ~/.ssh, and an absolute path would not survive a
83- * config synced between machines.
84- */
85- const CODER_SSH_CONFIG_PATH = "~/.ssh/coder/config" ;
86-
8782export interface RemoteDetails extends vscode . Disposable {
8883 safeHostname : string ;
8984 url : string ;
@@ -160,6 +155,16 @@ export class Remote {
160155 return ;
161156 }
162157
158+ switch ( classifyRemoteAuthority ( parts ) ) {
159+ case "current" :
160+ break ;
161+ case "legacy" :
162+ await this . migrateLegacyAuthority ( remoteAuthority , startupMode ) ;
163+ return ;
164+ case "foreign" :
165+ return ;
166+ }
167+
163168 this . logger . info ( "Setting up remote connection" , {
164169 remoteAuthority,
165170 hostname : parts . safeHostname ,
@@ -713,6 +718,46 @@ export class Remote {
713718 return undefined ;
714719 }
715720
721+ private async migrateLegacyAuthority (
722+ remoteAuthority : string ,
723+ startupMode : StartupMode ,
724+ ) : Promise < void > {
725+ const migratedAuthority = retargetRemoteAuthority ( remoteAuthority ) ;
726+ const workspaceFile = vscode . workspace . workspaceFile ;
727+ const workspaceFolders = vscode . workspace . workspaceFolders ?? [ ] ;
728+ const savedWorkspaceFile =
729+ workspaceFile ?. scheme === "untitled" ? undefined : workspaceFile ;
730+ if ( ! savedWorkspaceFile && workspaceFolders . length > 1 ) {
731+ throw new Error (
732+ "Cannot migrate a multi-root workspace without a saved workspace file." ,
733+ ) ;
734+ }
735+
736+ await this . serviceContainer
737+ . getMementoManager ( )
738+ . setStartupMode ( startupMode === "none" ? "start" : startupMode ) ;
739+ this . logger . info ( "Migrating legacy remote authority" , {
740+ from : remoteAuthority ,
741+ to : migratedAuthority ,
742+ } ) ;
743+
744+ const currentUri = savedWorkspaceFile ?? workspaceFolders [ 0 ] ?. uri ;
745+ if ( currentUri ) {
746+ await vscode . commands . executeCommand (
747+ "vscode.openFolder" ,
748+ currentUri . with ( {
749+ authority : retargetRemoteAuthority ( currentUri . authority ) ,
750+ } ) ,
751+ false ,
752+ ) ;
753+ return ;
754+ }
755+ await vscode . commands . executeCommand ( "vscode.newWindow" , {
756+ remoteAuthority : migratedAuthority ,
757+ reuseWindow : true ,
758+ } ) ;
759+ }
760+
716761 private async resolveRemoteBinary ( workspaceClient : Api ) : Promise < string > {
717762 if (
718763 this . extensionContext . extensionMode === vscode . ExtensionMode . Production
@@ -967,10 +1012,8 @@ export class Remote {
9671012 // Our blocks live in our own file; the user's only gains the include.
9681013 const sshConfig = new SshConfig ( this . getSshConfigPath ( ) , this . logger ) ;
9691014 await sshConfig . load ( ) ;
970- const coderConfig = new SshConfig (
971- expandPath ( CODER_SSH_CONFIG_PATH ) ,
972- this . logger ,
973- ) ;
1015+ const coderConfigPath = this . pathResolver . getSshConfigPath ( ) ;
1016+ const coderConfig = new SshConfig ( coderConfigPath , this . logger ) ;
9741017 await coderConfig . load ( ) ;
9751018
9761019 // Merge SSH config from three sources (highest to lowest priority):
@@ -988,9 +1031,7 @@ export class Remote {
9881031 userConfig ,
9891032 ) ;
9901033
991- const hostPrefix = safeHostname
992- ? `${ AuthorityPrefix } .${ safeHostname } --`
993- : `${ AuthorityPrefix } --` ;
1034+ const hostPrefix = toCurrentAuthorityHostPrefix ( safeHostname ) ;
9941035
9951036 const proxyCommand = await this . buildProxyCommand (
9961037 binaryPath ,
@@ -1019,7 +1060,13 @@ export class Remote {
10191060
10201061 // Write our file before including it, so the include never dangles.
10211062 await coderConfig . update ( safeHostname , sshValues , sshConfigOverrides ) ;
1022- await sshConfig . updateInclude ( CODER_SSH_CONFIG_PATH , safeHostname ) ;
1063+ await sshConfig . updateInclude (
1064+ {
1065+ id : vscode . env . uriScheme ,
1066+ includePath : coderConfigPath ,
1067+ } ,
1068+ safeHostname ,
1069+ ) ;
10231070
10241071 // Mirror SSH's parse order; RemoteCommand can come from the user's config.
10251072 return computeSshProperties (
0 commit comments