дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 11:15:40 +02:00
The remote server read the login shell's PATH once and cached it, had no project-level environment, and changed environment variables only took effect after reinstalling or a manual restart.
- Every openidea-server start re-reads the remote login shell's full environment (`$SHELL -ilc env -0`), with the cached PATH only as a fallback when the shell doesn't answer.
- A project's `.devcontainer/devcontainer.json` (or `.devcontainer.json`) applies on remote hosts: `remoteEnv` (with `${localEnv|containerEnv|env:X[:default]}`, workspace variables and `null` to unset) reaches every child process through one environment overlay (terminals, run configurations, language servers, debuggers, build tools, git), and `postStartCommand` (string, array or parallel object) runs once per server start in the project root as a cancellable background task; a failure raises a notification with the exit code and the last output lines without blocking the project.
- Reload Environment (`remote.reloadEnvironment`, File menu and Find Action) re-resolves the login shell and `remoteEnv` without restarting the server, starting from the process's original environment so prepended PATH entries don't pile up; it lists the changed variables, notes that running processes keep their environment, and offers to restart language servers.
- The JSONC reader moves to `ide_core::jsonc` and is shared with ide-run.
Claude-Session: https://claude.ai/code/session_01Dq6jQQn8ZhRfkkyj3PRutA
74 рядки
3.2 KiB
Rust
74 рядки
3.2 KiB
Rust
//! `openidea-server --stdio`: the IDE backend for remote development, serving
|
|
//! one desktop client over stdin / stdout (started by the client over SSH).
|
|
|
|
use ide_host::rpc::{self, Dispatcher};
|
|
use ide_host::{Host, HostConfig};
|
|
use ide_language::LanguageRegistry;
|
|
use std::path::PathBuf;
|
|
use std::sync::{Arc, OnceLock};
|
|
|
|
fn main() {
|
|
let args: Vec<String> = std::env::args().skip(1).collect();
|
|
if args.iter().any(|a| a == "--version") {
|
|
println!("openidea-server {} (protocol {})", env!("CARGO_PKG_VERSION"), rpc::PROTOCOL_VERSION);
|
|
return;
|
|
}
|
|
if !args.iter().any(|a| a == "--stdio") {
|
|
eprintln!("usage: openidea-server --stdio | --version");
|
|
std::process::exit(2);
|
|
}
|
|
// stdout carries the protocol: logs go to stderr.
|
|
tracing_subscriber::fmt()
|
|
.with_writer(std::io::stderr)
|
|
.with_env_filter(tracing_subscriber::EnvFilter::try_from_env("OPENIDEA_LOG").unwrap_or_else(|_| "info".into()))
|
|
.init();
|
|
let home = dirs::home_dir().unwrap_or_default();
|
|
let base = std::env::var_os("OPENIDEA_SERVER_HOME").map(PathBuf::from).unwrap_or_else(|| home.join(".openidea"));
|
|
// An SSH exec channel gets a minimal environment: take the login shell's, read
|
|
// afresh on every start, plus the IDE-managed tools. Before any other thread starts.
|
|
ide_host::shell_env::inherit_login_shell_env("openidea-server");
|
|
let data_dir = base.join("data");
|
|
ide_tools::extend_process_path(&data_dir);
|
|
ide_dap::adapter::set_tools_root(ide_tools::tools_dir(&data_dir));
|
|
ide_sdk::java::add_jdk_root(&ide_tools::jdk::jdks_dir(&data_dir));
|
|
let runtime = tokio::runtime::Builder::new_multi_thread().enable_all().build().expect("tokio runtime");
|
|
let code = runtime.block_on(run(base));
|
|
std::process::exit(code);
|
|
}
|
|
|
|
async fn run(base: PathBuf) -> i32 {
|
|
let (config_dir, data_dir) = (base.join("config"), base.join("data"));
|
|
for dir in [&config_dir, &data_dir] {
|
|
if let Err(e) = std::fs::create_dir_all(dir) {
|
|
eprintln!("cannot create {}: {e}", dir.display());
|
|
return 1;
|
|
}
|
|
}
|
|
let languages = match LanguageRegistry::load(Some(&config_dir.join("languages"))) {
|
|
Ok(l) => Arc::new(l),
|
|
Err(e) => {
|
|
eprintln!("language definitions: {e}");
|
|
return 1;
|
|
}
|
|
};
|
|
let peer = Arc::new(OnceLock::new());
|
|
let sink_peer = peer.clone();
|
|
let host = Arc::new(Host::new(HostConfig {
|
|
config_dir,
|
|
data_dir,
|
|
languages,
|
|
syntax: Arc::default(),
|
|
sink_for: Box::new(move |window| rpc::event_sink(sink_peer.clone(), Some(window.to_owned()))),
|
|
owner: |w| w,
|
|
runtime: tokio::runtime::Handle::current(),
|
|
project_environment: true,
|
|
}));
|
|
tracing::info!("openidea-server {} ready", env!("CARGO_PKG_VERSION"));
|
|
rpc::serve(host.clone(), Dispatcher::standard(), peer, tokio::io::stdin(), tokio::io::stdout()).await;
|
|
// `process::exit` skips destructors: nothing started for the client may outlive it.
|
|
if tokio::time::timeout(std::time::Duration::from_secs(10), host.shutdown()).await.is_err() {
|
|
tracing::warn!("language servers did not shut down in time");
|
|
}
|
|
0
|
|
}
|