WIP: ssh remoting: Add upload_binary
field to SshConnections (#19748)
This removes the old `remote_server { "download_binary_on_host": bool }` field and replaces it with a `upload_binary: bool` on every `ssh_connection`. @ConradIrwin it compiles, it connects, but I haven't tested it really yet Release Notes: - N/A --------- Co-authored-by: Conrad <conrad@zed.dev> Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
This commit is contained in:
parent
1acebb3c47
commit
fc8a72cdd8
11 changed files with 204 additions and 235 deletions
|
@ -11,7 +11,7 @@ use ui::{
|
|||
};
|
||||
use workspace::{notifications::DetachAndPromptErr, ModalView, OpenOptions, Workspace};
|
||||
|
||||
use crate::{open_ssh_project, SshSettings};
|
||||
use crate::open_ssh_project;
|
||||
|
||||
enum Host {
|
||||
RemoteProject,
|
||||
|
@ -102,16 +102,6 @@ impl DisconnectedOverlay {
|
|||
let paths = ssh_project.paths.iter().map(PathBuf::from).collect();
|
||||
|
||||
cx.spawn(move |_, mut cx| async move {
|
||||
let nickname = cx
|
||||
.update(|cx| {
|
||||
SshSettings::get_global(cx).nickname_for(
|
||||
&connection_options.host,
|
||||
connection_options.port,
|
||||
&connection_options.username,
|
||||
)
|
||||
})
|
||||
.ok()
|
||||
.flatten();
|
||||
open_ssh_project(
|
||||
connection_options,
|
||||
paths,
|
||||
|
@ -120,7 +110,6 @@ impl DisconnectedOverlay {
|
|||
replace_window: Some(window),
|
||||
..Default::default()
|
||||
},
|
||||
nickname,
|
||||
&mut cx,
|
||||
)
|
||||
.await?;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
pub mod disconnected_overlay;
|
||||
mod remote_servers;
|
||||
mod ssh_connections;
|
||||
use remote::SshConnectionOptions;
|
||||
pub use ssh_connections::open_ssh_project;
|
||||
|
||||
use disconnected_overlay::DisconnectedOverlay;
|
||||
|
@ -331,23 +330,12 @@ impl PickerDelegate for RecentProjectsDelegate {
|
|||
..Default::default()
|
||||
};
|
||||
|
||||
let args = SshSettings::get_global(cx).args_for(
|
||||
&ssh_project.host,
|
||||
ssh_project.port,
|
||||
&ssh_project.user,
|
||||
);
|
||||
let nickname = SshSettings::get_global(cx).nickname_for(
|
||||
&ssh_project.host,
|
||||
ssh_project.port,
|
||||
&ssh_project.user,
|
||||
);
|
||||
let connection_options = SshConnectionOptions {
|
||||
host: ssh_project.host.clone(),
|
||||
username: ssh_project.user.clone(),
|
||||
port: ssh_project.port,
|
||||
password: None,
|
||||
args,
|
||||
};
|
||||
let connection_options = SshSettings::get_global(cx)
|
||||
.connection_options_for(
|
||||
ssh_project.host.clone(),
|
||||
ssh_project.port,
|
||||
ssh_project.user.clone(),
|
||||
);
|
||||
|
||||
let paths = ssh_project.paths.iter().map(PathBuf::from).collect();
|
||||
|
||||
|
@ -357,7 +345,6 @@ impl PickerDelegate for RecentProjectsDelegate {
|
|||
paths,
|
||||
app_state,
|
||||
open_options,
|
||||
nickname,
|
||||
&mut cx,
|
||||
)
|
||||
.await
|
||||
|
|
|
@ -197,11 +197,7 @@ impl ProjectPicker {
|
|||
picker
|
||||
});
|
||||
let connection_string = connection.connection_string().into();
|
||||
let nickname = SshSettings::get_global(cx).nickname_for(
|
||||
&connection.host,
|
||||
connection.port,
|
||||
&connection.username,
|
||||
);
|
||||
let nickname = connection.nickname.clone().map(|nick| nick.into());
|
||||
let _path_task = cx
|
||||
.spawn({
|
||||
let workspace = workspace.clone();
|
||||
|
@ -414,7 +410,7 @@ impl RemoteServerProjects {
|
|||
return;
|
||||
}
|
||||
};
|
||||
let ssh_prompt = cx.new_view(|cx| SshPrompt::new(&connection_options, None, cx));
|
||||
let ssh_prompt = cx.new_view(|cx| SshPrompt::new(&connection_options, cx));
|
||||
|
||||
let connection = connect_over_ssh(
|
||||
connection_options.remote_server_identifier(),
|
||||
|
@ -491,12 +487,11 @@ impl RemoteServerProjects {
|
|||
return;
|
||||
};
|
||||
|
||||
let nickname = ssh_connection.nickname.clone();
|
||||
let connection_options = ssh_connection.into();
|
||||
workspace.update(cx, |_, cx| {
|
||||
cx.defer(move |workspace, cx| {
|
||||
workspace.toggle_modal(cx, |cx| {
|
||||
SshConnectionModal::new(&connection_options, Vec::new(), nickname, cx)
|
||||
SshConnectionModal::new(&connection_options, Vec::new(), cx)
|
||||
});
|
||||
let prompt = workspace
|
||||
.active_modal::<SshConnectionModal>(cx)
|
||||
|
@ -584,9 +579,7 @@ impl RemoteServerProjects {
|
|||
self.create_ssh_server(state.address_editor.clone(), cx);
|
||||
}
|
||||
Mode::EditNickname(state) => {
|
||||
let text = Some(state.editor.read(cx).text(cx))
|
||||
.filter(|text| !text.is_empty())
|
||||
.map(SharedString::from);
|
||||
let text = Some(state.editor.read(cx).text(cx)).filter(|text| !text.is_empty());
|
||||
let index = state.index;
|
||||
self.update_settings_file(cx, move |setting, _| {
|
||||
if let Some(connections) = setting.ssh_connections.as_mut() {
|
||||
|
@ -633,7 +626,7 @@ impl RemoteServerProjects {
|
|||
) -> impl IntoElement {
|
||||
let (main_label, aux_label) = if let Some(nickname) = ssh_connection.nickname.clone() {
|
||||
let aux_label = SharedString::from(format!("({})", ssh_connection.host));
|
||||
(nickname, Some(aux_label))
|
||||
(nickname.into(), Some(aux_label))
|
||||
} else {
|
||||
(ssh_connection.host.clone(), None)
|
||||
};
|
||||
|
@ -746,13 +739,11 @@ impl RemoteServerProjects {
|
|||
let project = project.clone();
|
||||
let server = server.clone();
|
||||
cx.spawn(|remote_server_projects, mut cx| async move {
|
||||
let nickname = server.nickname.clone();
|
||||
let result = open_ssh_project(
|
||||
server.into(),
|
||||
project.paths.into_iter().map(PathBuf::from).collect(),
|
||||
app_state,
|
||||
OpenOptions::default(),
|
||||
nickname,
|
||||
&mut cx,
|
||||
)
|
||||
.await;
|
||||
|
@ -861,6 +852,7 @@ impl RemoteServerProjects {
|
|||
projects: vec![],
|
||||
nickname: None,
|
||||
args: connection_options.args.unwrap_or_default(),
|
||||
upload_binary_over_ssh: None,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
@ -953,7 +945,7 @@ impl RemoteServerProjects {
|
|||
SshConnectionHeader {
|
||||
connection_string: connection_string.clone(),
|
||||
paths: Default::default(),
|
||||
nickname: connection.nickname.clone(),
|
||||
nickname: connection.nickname.clone().map(|s| s.into()),
|
||||
}
|
||||
.render(cx),
|
||||
)
|
||||
|
@ -1135,13 +1127,14 @@ impl RemoteServerProjects {
|
|||
};
|
||||
|
||||
let connection_string = connection.host.clone();
|
||||
let nickname = connection.nickname.clone().map(|s| s.into());
|
||||
|
||||
v_flex()
|
||||
.child(
|
||||
SshConnectionHeader {
|
||||
connection_string,
|
||||
paths: Default::default(),
|
||||
nickname: connection.nickname.clone(),
|
||||
nickname,
|
||||
}
|
||||
.render(cx),
|
||||
)
|
||||
|
|
|
@ -26,15 +26,9 @@ use ui::{
|
|||
};
|
||||
use workspace::{AppState, ModalView, Workspace};
|
||||
|
||||
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct RemoteServerSettings {
|
||||
pub download_on_host: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct SshSettings {
|
||||
pub ssh_connections: Option<Vec<SshConnection>>,
|
||||
pub remote_server: Option<RemoteServerSettings>,
|
||||
}
|
||||
|
||||
impl SshSettings {
|
||||
|
@ -42,39 +36,31 @@ impl SshSettings {
|
|||
self.ssh_connections.clone().into_iter().flatten()
|
||||
}
|
||||
|
||||
pub fn args_for(
|
||||
pub fn connection_options_for(
|
||||
&self,
|
||||
host: &str,
|
||||
host: String,
|
||||
port: Option<u16>,
|
||||
user: &Option<String>,
|
||||
) -> Option<Vec<String>> {
|
||||
self.ssh_connections()
|
||||
.filter_map(|conn| {
|
||||
if conn.host == host && &conn.username == user && conn.port == port {
|
||||
Some(conn.args)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.next()
|
||||
}
|
||||
|
||||
pub fn nickname_for(
|
||||
&self,
|
||||
host: &str,
|
||||
port: Option<u16>,
|
||||
user: &Option<String>,
|
||||
) -> Option<SharedString> {
|
||||
self.ssh_connections()
|
||||
.filter_map(|conn| {
|
||||
if conn.host == host && &conn.username == user && conn.port == port {
|
||||
Some(conn.nickname)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.next()
|
||||
.flatten()
|
||||
username: Option<String>,
|
||||
) -> SshConnectionOptions {
|
||||
for conn in self.ssh_connections() {
|
||||
if conn.host == host && conn.username == username && conn.port == port {
|
||||
return SshConnectionOptions {
|
||||
nickname: conn.nickname,
|
||||
upload_binary_over_ssh: conn.upload_binary_over_ssh.unwrap_or_default(),
|
||||
args: Some(conn.args),
|
||||
host,
|
||||
port,
|
||||
username,
|
||||
password: None,
|
||||
};
|
||||
}
|
||||
}
|
||||
SshConnectionOptions {
|
||||
host,
|
||||
port,
|
||||
username,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,13 +71,20 @@ pub struct SshConnection {
|
|||
pub username: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub port: Option<u16>,
|
||||
pub projects: Vec<SshProject>,
|
||||
/// Name to use for this server in UI.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub nickname: Option<SharedString>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
#[serde(default)]
|
||||
pub args: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub projects: Vec<SshProject>,
|
||||
/// Name to use for this server in UI.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub nickname: Option<String>,
|
||||
// By default Zed will download the binary to the host directly.
|
||||
// If this is set to true, Zed will download the binary to your local machine,
|
||||
// and then upload it over the SSH connection. Useful if your SSH server has
|
||||
// limited outbound internet access.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub upload_binary_over_ssh: Option<bool>,
|
||||
}
|
||||
|
||||
impl From<SshConnection> for SshConnectionOptions {
|
||||
|
@ -102,6 +95,8 @@ impl From<SshConnection> for SshConnectionOptions {
|
|||
port: val.port,
|
||||
password: None,
|
||||
args: Some(val.args),
|
||||
nickname: val.nickname,
|
||||
upload_binary_over_ssh: val.upload_binary_over_ssh.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -114,7 +109,6 @@ pub struct SshProject {
|
|||
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct RemoteSettingsContent {
|
||||
pub ssh_connections: Option<Vec<SshConnection>>,
|
||||
pub remote_server: Option<RemoteServerSettings>,
|
||||
}
|
||||
|
||||
impl Settings for SshSettings {
|
||||
|
@ -153,10 +147,10 @@ pub struct SshConnectionModal {
|
|||
impl SshPrompt {
|
||||
pub(crate) fn new(
|
||||
connection_options: &SshConnectionOptions,
|
||||
nickname: Option<SharedString>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
let connection_string = connection_options.connection_string().into();
|
||||
let nickname = connection_options.nickname.clone().map(|s| s.into());
|
||||
|
||||
Self {
|
||||
connection_string,
|
||||
|
@ -276,11 +270,10 @@ impl SshConnectionModal {
|
|||
pub(crate) fn new(
|
||||
connection_options: &SshConnectionOptions,
|
||||
paths: Vec<PathBuf>,
|
||||
nickname: Option<SharedString>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
Self {
|
||||
prompt: cx.new_view(|cx| SshPrompt::new(connection_options, nickname, cx)),
|
||||
prompt: cx.new_view(|cx| SshPrompt::new(connection_options, cx)),
|
||||
finished: false,
|
||||
paths,
|
||||
}
|
||||
|
@ -451,13 +444,17 @@ impl remote::SshClientDelegate for SshClientDelegate {
|
|||
fn get_server_binary(
|
||||
&self,
|
||||
platform: SshPlatform,
|
||||
upload_binary_over_ssh: bool,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> oneshot::Receiver<Result<(ServerBinary, SemanticVersion)>> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let this = self.clone();
|
||||
cx.spawn(|mut cx| async move {
|
||||
tx.send(this.get_server_binary_impl(platform, &mut cx).await)
|
||||
.ok();
|
||||
tx.send(
|
||||
this.get_server_binary_impl(platform, upload_binary_over_ssh, &mut cx)
|
||||
.await,
|
||||
)
|
||||
.ok();
|
||||
})
|
||||
.detach();
|
||||
rx
|
||||
|
@ -492,19 +489,14 @@ impl SshClientDelegate {
|
|||
async fn get_server_binary_impl(
|
||||
&self,
|
||||
platform: SshPlatform,
|
||||
upload_binary_via_ssh: bool,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> Result<(ServerBinary, SemanticVersion)> {
|
||||
let (version, release_channel, download_binary_on_host) = cx.update(|cx| {
|
||||
let (version, release_channel) = cx.update(|cx| {
|
||||
let version = AppVersion::global(cx);
|
||||
let channel = ReleaseChannel::global(cx);
|
||||
|
||||
let ssh_settings = SshSettings::get_global(cx);
|
||||
let download_binary_on_host = ssh_settings
|
||||
.remote_server
|
||||
.as_ref()
|
||||
.and_then(|server| server.download_on_host)
|
||||
.unwrap_or(false);
|
||||
(version, channel, download_binary_on_host)
|
||||
(version, channel)
|
||||
})?;
|
||||
|
||||
// In dev mode, build the remote server binary from source
|
||||
|
@ -529,33 +521,7 @@ impl SshClientDelegate {
|
|||
cx,
|
||||
);
|
||||
|
||||
if download_binary_on_host {
|
||||
let (request_url, request_body) = AutoUpdater::get_remote_server_release_url(
|
||||
platform.os,
|
||||
platform.arch,
|
||||
release_channel,
|
||||
current_version,
|
||||
cx,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
anyhow!(
|
||||
"Failed to get remote server binary download url (version: {}, os: {}, arch: {}): {}",
|
||||
version,
|
||||
platform.os,
|
||||
platform.arch,
|
||||
e
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok((
|
||||
ServerBinary::ReleaseUrl {
|
||||
url: request_url,
|
||||
body: request_body,
|
||||
},
|
||||
version,
|
||||
))
|
||||
} else {
|
||||
if upload_binary_via_ssh {
|
||||
let binary_path = AutoUpdater::download_remote_server_release(
|
||||
platform.os,
|
||||
platform.arch,
|
||||
|
@ -575,6 +541,32 @@ impl SshClientDelegate {
|
|||
})?;
|
||||
|
||||
Ok((ServerBinary::LocalBinary(binary_path), version))
|
||||
} else {
|
||||
let (request_url, request_body) = AutoUpdater::get_remote_server_release_url(
|
||||
platform.os,
|
||||
platform.arch,
|
||||
release_channel,
|
||||
current_version,
|
||||
cx,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
anyhow!(
|
||||
"Failed to get remote server binary download url (version: {}, os: {}, arch: {}): {}",
|
||||
version,
|
||||
platform.os,
|
||||
platform.arch,
|
||||
e
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok((
|
||||
ServerBinary::ReleaseUrl {
|
||||
url: request_url,
|
||||
body: request_body,
|
||||
},
|
||||
version,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -715,7 +707,6 @@ pub async fn open_ssh_project(
|
|||
paths: Vec<PathBuf>,
|
||||
app_state: Arc<AppState>,
|
||||
open_options: workspace::OpenOptions,
|
||||
nickname: Option<SharedString>,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> Result<()> {
|
||||
let window = if let Some(window) = open_options.replace_window {
|
||||
|
@ -740,12 +731,11 @@ pub async fn open_ssh_project(
|
|||
let (cancel_tx, cancel_rx) = oneshot::channel();
|
||||
let delegate = window.update(cx, {
|
||||
let connection_options = connection_options.clone();
|
||||
let nickname = nickname.clone();
|
||||
let paths = paths.clone();
|
||||
move |workspace, cx| {
|
||||
cx.activate_window();
|
||||
workspace.toggle_modal(cx, |cx| {
|
||||
SshConnectionModal::new(&connection_options, paths, nickname.clone(), cx)
|
||||
SshConnectionModal::new(&connection_options, paths, cx)
|
||||
});
|
||||
|
||||
let ui = workspace
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue