diff --git a/src/bin/dosh-client.rs b/src/bin/dosh-client.rs index b09c194..e9a22e4 100644 --- a/src/bin/dosh-client.rs +++ b/src/bin/dosh-client.rs @@ -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, 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");