Avoid derived UDP host in SSH imports
ci / test (push) Canceled after 0s
ci / fuzz-smoke (push) Canceled after 0s
ci / macos-client (macos-aarch64, macos-14) (push) Canceled after 0s
ci / macos-client (macos-x86_64, macos-13) (push) Canceled after 0s
ci / windows-client (push) Canceled after 0s
ci / package-release (linux-x86_64, ubuntu-latest, , , ) (push) Canceled after 0s
ci / package-release (macos-aarch64, macos-14, , , ) (push) Canceled after 0s
ci / package-release (macos-x86_64, macos-13, , , ) (push) Canceled after 0s
ci / package-release (windows-aarch64, windows-latest, aarch64, windows, aarch64-pc-windows-msvc) (push) Canceled after 0s
ci / package-release (windows-x86_64, windows-latest, , , ) (push) Canceled after 0s
ci / remote-bench (push) Canceled after 0s
ci / publish-gitea-release (push) Canceled after 0s

This commit is contained in:
DuProcess
2026-07-16 21:26:44 -04:00
parent af3881e205
commit 249cf5e836
+88 -37
View File
@@ -752,7 +752,11 @@ async fn run_setup_command(config: &dosh::config::ClientConfig, args: &Args) ->
match entry.action {
ImportAction::Added => println!(
"[ok] host config: [{}] ssh={} ssh_config={} dosh_host={} port={}",
entry.alias, entry.ssh, entry.ssh_config, entry.dosh_host, entry.port
entry.alias,
entry.ssh,
entry.ssh_config,
imported_dosh_host_display(&entry),
entry.port
),
ImportAction::Skipped => println!("[ok] host config: [{}] already exists", entry.alias),
}
@@ -4858,7 +4862,7 @@ struct ImportedHost {
alias: String,
ssh: String,
ssh_config: String,
dosh_host: String,
dosh_host: Option<String>,
port: u16,
action: ImportAction,
}
@@ -4870,10 +4874,19 @@ fn run_import_ssh(config: &dosh::config::ClientConfig, aliases: &[String]) -> Re
let imported = import_ssh_aliases(config, aliases)?;
for entry in imported {
match entry.action {
ImportAction::Added => eprintln!(
"dosh import-ssh: added [{}] ssh={} ssh_config={} dosh_host={}",
entry.alias, entry.ssh, entry.ssh_config, entry.dosh_host
),
ImportAction::Added => {
if let Some(dosh_host) = &entry.dosh_host {
eprintln!(
"dosh import-ssh: added [{}] ssh={} ssh_config={} dosh_host={}",
entry.alias, entry.ssh, entry.ssh_config, dosh_host
)
} else {
eprintln!(
"dosh import-ssh: added [{}] ssh={} ssh_config={} dosh_host=ssh",
entry.alias, entry.ssh, entry.ssh_config
)
}
}
ImportAction::Skipped => {
eprintln!(
"dosh import-ssh: [{}] already exists, skipping",
@@ -4904,46 +4917,23 @@ fn import_ssh_aliases(
}
let mut imported = Vec::new();
for alias in aliases {
let ssh_config = ssh_config(alias, None)
.with_context(|| format!("read SSH config for {alias} with ssh -G"))?;
let udp_host = ssh_config
.hostname
.clone()
.unwrap_or_else(|| ssh_destination_host(alias));
if raw_contains_host_table(&raw, alias) {
imported.push(ImportedHost {
alias: alias.clone(),
ssh: alias.clone(),
ssh_config: alias.clone(),
dosh_host: udp_host,
dosh_host: None,
port: config.dosh_port,
action: ImportAction::Skipped,
});
continue;
}
let ssh_config = ssh_config(alias, None)
.with_context(|| format!("read SSH config for {alias} with ssh -G"))?;
let (block, entry) = imported_host_block(config, alias, &ssh_config);
raw.push('\n');
raw.push_str(&format!("[{}]\n", toml_bare_key_or_quoted(alias)));
raw.push_str(&format!("ssh = {}\n", toml_string(alias)));
raw.push_str(&format!("ssh_config = {}\n", toml_string(alias)));
raw.push_str(&format!("dosh_host = {}\n", toml_string(&udp_host)));
raw.push_str(&format!("port = {}\n", config.dosh_port));
if let Some(user) = &ssh_config.user {
raw.push_str(&format!("user = {}\n", toml_string(user)));
}
if let Some(port) = ssh_config.port
&& port != 22
{
raw.push_str(&format!("ssh_port = {port}\n"));
}
raw.push_str("predict = true\n");
imported.push(ImportedHost {
alias: alias.clone(),
ssh: alias.clone(),
ssh_config: alias.clone(),
dosh_host: udp_host,
port: config.dosh_port,
action: ImportAction::Added,
});
raw.push_str(&block);
imported.push(entry);
}
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?;
@@ -4952,6 +4942,43 @@ fn import_ssh_aliases(
Ok(imported)
}
fn imported_host_block(
config: &dosh::config::ClientConfig,
alias: &str,
ssh_config: &SshConfig,
) -> (String, ImportedHost) {
let mut raw = String::new();
raw.push_str(&format!("[{}]\n", toml_bare_key_or_quoted(alias)));
raw.push_str(&format!("ssh = {}\n", toml_string(alias)));
raw.push_str(&format!("ssh_config = {}\n", toml_string(alias)));
raw.push_str("# dosh_host = \"server.example.com\"\n");
raw.push_str(&format!("port = {}\n", config.dosh_port));
if let Some(user) = &ssh_config.user {
raw.push_str(&format!("user = {}\n", toml_string(user)));
}
if let Some(port) = ssh_config.port
&& port != 22
{
raw.push_str(&format!("ssh_port = {port}\n"));
}
raw.push_str("predict = true\n");
(
raw,
ImportedHost {
alias: alias.to_string(),
ssh: alias.to_string(),
ssh_config: alias.to_string(),
dosh_host: None,
port: config.dosh_port,
action: ImportAction::Added,
},
)
}
fn imported_dosh_host_display(entry: &ImportedHost) -> &str {
entry.dosh_host.as_deref().unwrap_or("ssh")
}
fn raw_contains_host_table(raw: &str, alias: &str) -> bool {
let quoted = toml_string(alias);
raw.lines().any(|line| {
@@ -10616,8 +10643,8 @@ mod tests {
TERMINAL_SNAPSHOT_RESET, UpdateOptions, UpdateRole, auth_allows, cache_key,
cache_server_prefix, cleanup_stream_state, clear_cached_credentials,
effective_update_artifact_tag, ensure_tui_safe_status_overlay, expand_ssh_path_tokens,
first_resolved_addr, input_contains_focus_in, input_matches_escape, is_local_status_target,
is_resume_response_for_client, latest_release_download_url,
first_resolved_addr, imported_host_block, input_contains_focus_in, input_matches_escape,
is_local_status_target, is_resume_response_for_client, latest_release_download_url,
load_first_native_identity_with_prompt, local_symlink_target_is_dir,
local_username_from_env, native_proxy_udp_warning, newest_client_trace_path_from,
parse_dynamic_forward, parse_escape_key, parse_local_forward, parse_remote_forward,
@@ -11155,6 +11182,30 @@ mod tests {
assert_eq!(toml_bare_key_or_quoted("home box"), "\"home box\"");
}
#[test]
fn imported_ssh_host_block_does_not_bake_derived_udp_host() {
let config = ClientConfig {
dosh_port: 50000,
..ClientConfig::default()
};
let ssh_config = SshConfig {
hostname: Some("10.0.0.5".to_string()),
user: Some("deploy".to_string()),
port: Some(2222),
..SshConfig::default()
};
let (block, imported) = imported_host_block(&config, "prod", &ssh_config);
assert!(block.contains("[prod]\n"));
assert!(block.contains("ssh_config = \"prod\"\n"));
assert!(block.contains("# dosh_host = \"server.example.com\"\n"));
assert!(!block.contains("dosh_host = \"10.0.0.5\""));
assert!(block.contains("user = \"deploy\"\n"));
assert!(block.contains("ssh_port = 2222\n"));
assert_eq!(imported.dosh_host, None);
}
#[test]
fn strips_user_from_fallback_udp_host() {
assert_eq!(ssh_destination_host("alice@example.com"), "example.com");