Add the ability to edit remote directories over SSH (#14530)

This is a first step towards allowing you to edit remote projects
directly over SSH. We'll start with a pretty bare-bones feature set, and
incrementally add further features.

### Todo

Distribution
* [x] Build nightly releases of `zed-remote-server` binaries
    * [x] linux (arm + x86)
    * [x] mac (arm + x86)
* [x] Build stable + preview releases of `zed-remote-server`
* [x] download and cache remote server binaries as needed when opening
ssh project
* [x] ensure server has the latest version of the binary


Auth
* [x] allow specifying password at the command line
* [x] auth via ssh keys
* [x] UI password prompt

Features
* [x] upload remote server binary to server automatically
* [x] opening directories
* [x] tracking file system updates
* [x] opening, editing, saving buffers
* [ ] file operations (rename, delete, create)
* [ ] git diffs
* [ ] project search

Release Notes:

- N/A

---------

Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
This commit is contained in:
Max Brunsfeld 2024-07-19 10:27:26 -07:00 committed by GitHub
parent 7733bf686b
commit b9a53ffa0b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
50 changed files with 2194 additions and 250 deletions

View file

@ -0,0 +1,78 @@
use fs::RealFs;
use futures::channel::mpsc;
use gpui::Context as _;
use remote::{
protocol::{read_message, write_message},
SshSession,
};
use remote_server::HeadlessProject;
use smol::{io::AsyncWriteExt, stream::StreamExt as _, Async};
use std::{env, io, mem, process, sync::Arc};
fn main() {
env::set_var("RUST_BACKTRACE", "1");
env::set_var("RUST_LOG", "remote=trace");
let subcommand = std::env::args().nth(1);
match subcommand.as_deref() {
Some("run") => {}
Some("version") => {
println!("{}", env!("ZED_PKG_VERSION"));
return;
}
_ => {
eprintln!("usage: remote <run|version>");
process::exit(1);
}
}
env_logger::init();
gpui::App::headless().run(move |cx| {
HeadlessProject::init(cx);
let (incoming_tx, incoming_rx) = mpsc::unbounded();
let (outgoing_tx, mut outgoing_rx) = mpsc::unbounded();
let mut stdin = Async::new(io::stdin()).unwrap();
let mut stdout = Async::new(io::stdout()).unwrap();
let session = SshSession::server(incoming_rx, outgoing_tx, cx);
let project = cx.new_model(|cx| {
HeadlessProject::new(
session.clone(),
Arc::new(RealFs::new(Default::default(), None)),
cx,
)
});
cx.background_executor()
.spawn(async move {
let mut output_buffer = Vec::new();
while let Some(message) = outgoing_rx.next().await {
write_message(&mut stdout, &mut output_buffer, message).await?;
stdout.flush().await?;
}
anyhow::Ok(())
})
.detach();
cx.background_executor()
.spawn(async move {
let mut input_buffer = Vec::new();
loop {
let message = match read_message(&mut stdin, &mut input_buffer).await {
Ok(message) => message,
Err(error) => {
log::warn!("error reading message: {:?}", error);
process::exit(0);
}
};
incoming_tx.unbounded_send(message).ok();
}
})
.detach();
mem::forget(project);
});
}