diff --git a/Cargo.lock b/Cargo.lock index 836584de8a..63749c9e2d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14765,7 +14765,6 @@ dependencies = [ "parking_lot", "postage", "project", - "release_channel", "remote", "schemars", "serde", diff --git a/crates/recent_projects/src/remote_servers.rs b/crates/recent_projects/src/remote_servers.rs index 7806459ed1..7581267228 100644 --- a/crates/recent_projects/src/remote_servers.rs +++ b/crates/recent_projects/src/remote_servers.rs @@ -16,6 +16,7 @@ use gpui::{ }; use picker::Picker; use project::Project; +use remote::ssh_session::ConnectionIdentifier; use remote::SshConnectionOptions; use remote::SshRemoteClient; use settings::update_settings_file; @@ -384,7 +385,7 @@ impl RemoteServerProjects { let ssh_prompt = cx.new_view(|cx| SshPrompt::new(&connection_options, cx)); let connection = connect_over_ssh( - connection_options.remote_server_identifier(), + ConnectionIdentifier::Setup, connection_options.clone(), ssh_prompt.clone(), cx, @@ -475,7 +476,7 @@ impl RemoteServerProjects { .clone(); let connect = connect_over_ssh( - connection_options.remote_server_identifier(), + ConnectionIdentifier::Setup, connection_options.clone(), prompt, cx, diff --git a/crates/recent_projects/src/ssh_connections.rs b/crates/recent_projects/src/ssh_connections.rs index f84576a1d9..e01cc3116d 100644 --- a/crates/recent_projects/src/ssh_connections.rs +++ b/crates/recent_projects/src/ssh_connections.rs @@ -14,6 +14,7 @@ use gpui::{AppContext, Model}; use language::CursorShape; use markdown::{Markdown, MarkdownStyle}; use release_channel::ReleaseChannel; +use remote::ssh_session::ConnectionIdentifier; use remote::{SshConnectionOptions, SshPlatform, SshRemoteClient}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -534,7 +535,7 @@ pub fn is_connecting_over_ssh(workspace: &Workspace, cx: &AppContext) -> bool { } pub fn connect_over_ssh( - unique_identifier: String, + unique_identifier: ConnectionIdentifier, connection_options: SshConnectionOptions, ui: View, cx: &mut WindowContext, diff --git a/crates/remote/src/ssh_session.rs b/crates/remote/src/ssh_session.rs index 9cae283749..0c49361adc 100644 --- a/crates/remote/src/ssh_session.rs +++ b/crates/remote/src/ssh_session.rs @@ -202,17 +202,6 @@ impl SshConnectionOptions { host } } - - // Uniquely identifies dev server projects on a remote host. Needs to be - // stable for the same dev server project. - pub fn remote_server_identifier(&self) -> String { - let mut identifier = format!("dev-server-{:?}", self.host); - if let Some(username) = self.username.as_ref() { - identifier.push('-'); - identifier.push_str(&username); - } - identifier - } } #[derive(Copy, Clone, Debug)] @@ -520,14 +509,43 @@ pub enum SshRemoteEvent { impl EventEmitter for SshRemoteClient {} +// Identifies the socket on the remote server so that reconnects +// can re-join the same project. +pub enum ConnectionIdentifier { + Setup, + Workspace(i64), +} + +impl ConnectionIdentifier { + // This string gets used in a socket name, and so must be relatively short. + // The total length of: + // /home/{username}/.local/share/zed/server_state/{name}/stdout.sock + // Must be less than about 100 characters + // https://unix.stackexchange.com/questions/367008/why-is-socket-path-length-limited-to-a-hundred-chars + // So our strings should be at most 20 characters or so. + fn to_string(&self, cx: &AppContext) -> String { + let identifier_prefix = match ReleaseChannel::global(cx) { + ReleaseChannel::Stable => "".to_string(), + release_channel => format!("{}-", release_channel.dev_name()), + }; + match self { + Self::Setup => format!("{identifier_prefix}setup"), + Self::Workspace(workspace_id) => { + format!("{identifier_prefix}workspace-{workspace_id}",) + } + } + } +} + impl SshRemoteClient { pub fn new( - unique_identifier: String, + unique_identifier: ConnectionIdentifier, connection_options: SshConnectionOptions, cancellation: oneshot::Receiver<()>, delegate: Arc, cx: &mut AppContext, ) -> Task>>> { + let unique_identifier = unique_identifier.to_string(cx); cx.spawn(|mut cx| async move { let success = Box::pin(async move { let (outgoing_tx, outgoing_rx) = mpsc::unbounded::(); @@ -1096,7 +1114,15 @@ impl SshRemoteClient { ) -> Model { let (_tx, rx) = oneshot::channel(); client_cx - .update(|cx| Self::new("fake".to_string(), opts, rx, Arc::new(fake::Delegate), cx)) + .update(|cx| { + Self::new( + ConnectionIdentifier::Setup, + opts, + rx, + Arc::new(fake::Delegate), + cx, + ) + }) .await .unwrap() .unwrap() diff --git a/crates/remote_server/src/remote_editing_tests.rs b/crates/remote_server/src/remote_editing_tests.rs index 2554aa48d2..a09d87f7c0 100644 --- a/crates/remote_server/src/remote_editing_tests.rs +++ b/crates/remote_server/src/remote_editing_tests.rs @@ -2,7 +2,7 @@ use crate::headless_project::HeadlessProject; use client::{Client, UserStore}; use clock::FakeSystemClock; use fs::{FakeFs, Fs}; -use gpui::{Context, Model, TestAppContext}; +use gpui::{Context, Model, SemanticVersion, TestAppContext}; use http_client::{BlockedHttpClient, FakeHttpClient}; use language::{ language_settings::{language_settings, AllLanguageSettings}, @@ -1184,6 +1184,9 @@ pub async fn init_test( server_cx: &mut TestAppContext, ) -> (Model, Model) { let server_fs = server_fs.clone(); + cx.update(|cx| { + release_channel::init(SemanticVersion::default(), cx); + }); init_logger(); let (opts, ssh_server_client) = SshRemoteClient::fake_server(cx, server_cx); diff --git a/crates/workspace/Cargo.toml b/crates/workspace/Cargo.toml index 6e3a4a7970..94a976dcd1 100644 --- a/crates/workspace/Cargo.toml +++ b/crates/workspace/Cargo.toml @@ -49,7 +49,6 @@ parking_lot.workspace = true postage.workspace = true project.workspace = true task.workspace = true -release_channel.workspace = true remote.workspace = true schemars.workspace = true serde.workspace = true diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 9715381684..fa9fef3deb 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -64,8 +64,7 @@ use postage::stream::Stream; use project::{ DirectoryLister, Project, ProjectEntryId, ProjectPath, ResolvedPath, Worktree, WorktreeId, }; -use release_channel::ReleaseChannel; -use remote::{SshClientDelegate, SshConnectionOptions}; +use remote::{ssh_session::ConnectionIdentifier, SshClientDelegate, SshConnectionOptions}; use serde::Deserialize; use session::AppSession; use settings::Settings; @@ -5496,26 +5495,14 @@ pub fn open_ssh_project( paths: Vec, cx: &mut AppContext, ) -> Task> { - let release_channel = ReleaseChannel::global(cx); - cx.spawn(|mut cx| async move { let (serialized_ssh_project, workspace_id, serialized_workspace) = serialize_ssh_project(connection_options.clone(), paths.clone(), &cx).await?; - let identifier_prefix = match release_channel { - ReleaseChannel::Stable => None, - _ => Some(format!("{}-", release_channel.dev_name())), - }; - let unique_identifier = format!( - "{}workspace-{}", - identifier_prefix.unwrap_or_default(), - workspace_id.0 - ); - let session = match cx .update(|cx| { remote::SshRemoteClient::new( - unique_identifier, + ConnectionIdentifier::Workspace(workspace_id.0), connection_options, cancel_rx, delegate,