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:
Thorsten Ball 2024-10-26 01:32:54 +02:00 committed by GitHub
parent 1acebb3c47
commit fc8a72cdd8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 204 additions and 235 deletions

View file

@ -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