@@ -40,10 +40,9 @@ import { getHeaderCommand } from "../settings/headers";
4040import { escapeCommandArg , expandPath } from "../util" ;
4141import {
4242 type AuthorityParts ,
43- classifyRemoteAuthority ,
43+ classifySshHost ,
4444 parseRemoteAuthority ,
4545 retargetRemoteAuthority ,
46- toCurrentAuthorityHostPrefix ,
4746} from "../util/authority" ;
4847import { createStatusBarItem } from "../util/statusBar" ;
4948import { vscodeProposed } from "../vscodeProposed" ;
@@ -54,7 +53,6 @@ import { migrateAuthToSecretsStorage } from "./migration";
5453import {
5554 SshConfig ,
5655 type SshValues ,
57- cleanupStaleSshConfigs ,
5856 mergeSshConfigValues ,
5957 parseCoderSshOptions ,
6058 parseSshConfig ,
@@ -164,9 +162,11 @@ export class Remote {
164162
165163 // parseRemoteAuthority returned null for foreign hosts, so this is
166164 // either the current editor's authority or a migratable legacy one.
167- if ( classifyRemoteAuthority ( parts ) === "legacy" ) {
168- await this . migrateLegacyAuthority ( remoteAuthority , startupMode ) ;
169- return ;
165+ if ( classifySshHost ( parts . sshHost ) === "legacy" ) {
166+ if ( await this . migrateLegacyAuthority ( remoteAuthority , startupMode ) ) {
167+ return ;
168+ }
169+ // Not reopened: keep going so the legacy host still connects.
170170 }
171171
172172 this . logger . info ( "Setting up remote connection" , {
@@ -676,8 +676,7 @@ export class Remote {
676676 this . logger . info ( "Updating SSH config..." ) ;
677677 return await this . updateSSHConfig (
678678 workspaceClient ,
679- context . parts . safeHostname ,
680- context . parts . sshHost ,
679+ context . parts ,
681680 binaryPath ,
682681 logDir ,
683682 featureSet ,
@@ -727,39 +726,42 @@ export class Remote {
727726 return undefined ;
728727 }
729728
729+ /**
730+ * Reopen the window on this editor's own authority. Returns false when the
731+ * workspace cannot be reopened losslessly, so the caller connects over the
732+ * legacy host instead.
733+ */
730734 private async migrateLegacyAuthority (
731735 remoteAuthority : string ,
732736 startupMode : StartupMode ,
733- ) : Promise < void > {
737+ ) : Promise < boolean > {
734738 const migratedAuthority = retargetRemoteAuthority ( remoteAuthority ) ;
735739 const workspaceFile = vscode . workspace . workspaceFile ;
736740 const workspaceFolders = vscode . workspace . workspaceFolders ?? [ ] ;
737741 const savedWorkspaceFile =
738742 workspaceFile ?. scheme === "untitled" ? undefined : workspaceFile ;
739743 if ( ! savedWorkspaceFile && workspaceFolders . length > 1 ) {
740744 this . logger . warn (
741- "Cannot migrate an unsaved multi-root workspace" ,
745+ "Cannot migrate an unsaved multi-root workspace; connecting over the legacy host " ,
742746 remoteAuthority ,
743747 ) ;
744- const choice = await vscodeProposed . window . showWarningMessage (
745- "Opening the remote over the old coder-vscode SSH host" ,
746- {
747- modal : true ,
748- useCustom : true ,
749- detail :
750- "This editor now uses its own SSH hosts, but switching an unsaved multi-root workspace would drop its folders. " +
751- "To switch, save the workspace, then reload the window." ,
752- } ,
753- "Learn More" ,
754- ) ;
755- if ( choice === "Learn More" ) {
756- await vscode . env . openExternal (
757- vscode . Uri . parse (
758- "https://code.visualstudio.com/docs/editing/workspaces/multi-root-workspaces" ,
759- ) ,
760- ) ;
761- }
762- return ;
748+ // Fire-and-forget: the connection proceeds either way.
749+ void vscode . window
750+ . showWarningMessage (
751+ "This workspace still opens over the old coder-vscode SSH host. " +
752+ "To switch it to this editor's own host, save the workspace, then reload the window." ,
753+ "Learn More" ,
754+ )
755+ . then ( async ( choice ) => {
756+ if ( choice === "Learn More" ) {
757+ await vscode . env . openExternal (
758+ vscode . Uri . parse (
759+ "https://code.visualstudio.com/docs/editing/workspaces/multi-root-workspaces" ,
760+ ) ,
761+ ) ;
762+ }
763+ } ) ;
764+ return false ;
763765 }
764766
765767 await this . serviceContainer
@@ -779,12 +781,13 @@ export class Remote {
779781 } ) ,
780782 false ,
781783 ) ;
782- return ;
784+ return true ;
783785 }
784786 await vscode . commands . executeCommand ( "vscode.newWindow" , {
785787 remoteAuthority : migratedAuthority ,
786788 reuseWindow : true ,
787789 } ) ;
790+ return true ;
788791 }
789792
790793 private async resolveRemoteBinary ( workspaceClient : Api ) : Promise < string > {
@@ -959,13 +962,14 @@ export class Remote {
959962 // all Coder entries.
960963 private async updateSSHConfig (
961964 restClient : Api ,
962- safeHostname : string ,
963- hostName : string ,
965+ parts : AuthorityParts ,
964966 binaryPath : string ,
965967 logDir : string ,
966968 featureSet : FeatureSet ,
967969 cliAuth : CliAuth ,
968970 ) : Promise < SshProperties > {
971+ // Taken from the authority, so an unmigrated legacy host keeps working.
972+ const { hostPrefix, safeHostname } = parts ;
969973 // One file per (editor, deployment); the user's config gains one shared include.
970974 const sshConfig = new SshConfig ( this . getMainSshConfigPath ( ) , this . logger ) ;
971975 await sshConfig . load ( ) ;
@@ -1017,8 +1021,6 @@ export class Remote {
10171021 userConfig ,
10181022 ) ;
10191023
1020- const hostPrefix = toCurrentAuthorityHostPrefix ( safeHostname ) ;
1021-
10221024 const proxyCommand = await this . buildProxyCommand (
10231025 binaryPath ,
10241026 safeHostname ,
@@ -1046,15 +1048,14 @@ export class Remote {
10461048
10471049 // Write our file before including it, so the include never dangles.
10481050 await coderConfig . update ( sshValues , sshConfigOverrides ) ;
1049- const sharedSshConfigDir = this . pathResolver . getSshConfigDir ( ) ;
1050- await sshConfig . updateInclude ( sharedSshConfigDir , safeHostname ) ;
1051- // Our file was just written, so only other unused deployments are swept.
1052- // Never throws, and the connection does not depend on it.
1053- void cleanupStaleSshConfigs ( sharedSshConfigDir , this . logger ) ;
1051+ await sshConfig . updateInclude (
1052+ this . pathResolver . getSshConfigDir ( ) ,
1053+ safeHostname ,
1054+ ) ;
10541055
10551056 // Mirror SSH's parse order; RemoteCommand can come from the user's config.
10561057 return computeSshProperties (
1057- hostName ,
1058+ parts . sshHost ,
10581059 `${ coderConfig . getRaw ( ) } \n${ sshConfig . getRaw ( ) } ` ,
10591060 ) ;
10601061 }
0 commit comments