Support remote server updates from Windows
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
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:
+153
-40
@@ -301,7 +301,7 @@ async fn main() -> Result<()> {
|
|||||||
return run_file_cat_command(&args);
|
return run_file_cat_command(&args);
|
||||||
}
|
}
|
||||||
if args.server.as_deref() == Some("update") {
|
if args.server.as_deref() == Some("update") {
|
||||||
return run_update(&config, parse_update_options(&args.command)?);
|
return run_update(&config, &args, parse_update_options(&args.command)?);
|
||||||
}
|
}
|
||||||
if args.server.as_deref() == Some("trace") {
|
if args.server.as_deref() == Some("trace") {
|
||||||
return run_trace_command(&args);
|
return run_trace_command(&args);
|
||||||
@@ -4097,16 +4097,18 @@ impl UpdateRole {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn installer_arg_for_os(self, os: &str) -> Result<&'static str> {
|
fn local_installer_arg_for_os(self, os: &str) -> Option<&'static str> {
|
||||||
if os == "windows" {
|
if os == "windows" {
|
||||||
return match self {
|
return match self {
|
||||||
UpdateRole::Auto | UpdateRole::Client => Ok("client"),
|
UpdateRole::Auto | UpdateRole::Client | UpdateRole::Both => Some("client"),
|
||||||
UpdateRole::Server | UpdateRole::Both => {
|
UpdateRole::Server => None,
|
||||||
Err(anyhow!("Windows installs support client updates only"))
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Ok(self.installer_arg())
|
Some(self.installer_arg())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn updates_remote_server_from_os(self, os: &str) -> bool {
|
||||||
|
os == "windows" && matches!(self, UpdateRole::Server | UpdateRole::Both)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4423,7 +4425,11 @@ fn url_reachable(url: &str) -> Result<bool> {
|
|||||||
Ok(status.success())
|
Ok(status.success())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_update(config: &dosh::config::ClientConfig, options: UpdateOptions) -> Result<()> {
|
fn run_update(
|
||||||
|
config: &dosh::config::ClientConfig,
|
||||||
|
args: &Args,
|
||||||
|
options: UpdateOptions,
|
||||||
|
) -> Result<()> {
|
||||||
let repo = config
|
let repo = config
|
||||||
.update_repo
|
.update_repo
|
||||||
.clone()
|
.clone()
|
||||||
@@ -4432,14 +4438,24 @@ fn run_update(config: &dosh::config::ClientConfig, options: UpdateOptions) -> Re
|
|||||||
let os = std::env::consts::OS;
|
let os = std::env::consts::OS;
|
||||||
let installer = update_installer_url(&raw_base, os);
|
let installer = update_installer_url(&raw_base, os);
|
||||||
let artifact = release_artifact_name();
|
let artifact = release_artifact_name();
|
||||||
let installer_role = options.role.installer_arg_for_os(os)?;
|
let local_installer_role = options.role.local_installer_arg_for_os(os);
|
||||||
|
let update_remote_server = options.role.updates_remote_server_from_os(os);
|
||||||
if options.check_only {
|
if options.check_only {
|
||||||
let local_version = env!("CARGO_PKG_VERSION");
|
let local_version = env!("CARGO_PKG_VERSION");
|
||||||
let latest_tag = latest_release_tag(&repo)?;
|
let latest_tag = latest_release_tag(&repo)?;
|
||||||
println!("dosh {local_version}");
|
println!("dosh {local_version}");
|
||||||
println!("repo: {repo}");
|
println!("repo: {repo}");
|
||||||
|
if let Some(local_installer_role) = local_installer_role {
|
||||||
println!("installer: {installer}");
|
println!("installer: {installer}");
|
||||||
println!("role: {installer_role}");
|
println!("role: {local_installer_role}");
|
||||||
|
}
|
||||||
|
if update_remote_server {
|
||||||
|
println!(
|
||||||
|
"remote_installer: {}",
|
||||||
|
update_installer_url(&raw_base, "linux")
|
||||||
|
);
|
||||||
|
println!("remote_server: {}", config.server);
|
||||||
|
}
|
||||||
match latest_tag.as_deref() {
|
match latest_tag.as_deref() {
|
||||||
Some(tag) => {
|
Some(tag) => {
|
||||||
let latest_version = release_version_from_tag(tag);
|
let latest_version = release_version_from_tag(tag);
|
||||||
@@ -4450,7 +4466,9 @@ fn run_update(config: &dosh::config::ClientConfig, options: UpdateOptions) -> Re
|
|||||||
}
|
}
|
||||||
None => println!("latest: unknown"),
|
None => println!("latest: unknown"),
|
||||||
}
|
}
|
||||||
if let Some(latest_url) = latest_release_download_url(&repo, &artifact) {
|
if let Some(latest_url) =
|
||||||
|
local_installer_role.and_then(|_| latest_release_download_url(&repo, &artifact))
|
||||||
|
{
|
||||||
let mut status = "missing";
|
let mut status = "missing";
|
||||||
let mut display_url = latest_url.clone();
|
let mut display_url = latest_url.clone();
|
||||||
if let Some(tag_url) =
|
if let Some(tag_url) =
|
||||||
@@ -4481,15 +4499,27 @@ fn run_update(config: &dosh::config::ClientConfig, options: UpdateOptions) -> Re
|
|||||||
env!("CARGO_PKG_VERSION"),
|
env!("CARGO_PKG_VERSION"),
|
||||||
latest_release_tag(&repo)?.as_deref(),
|
latest_release_tag(&repo)?.as_deref(),
|
||||||
);
|
);
|
||||||
|
if update_remote_server {
|
||||||
|
run_remote_server_update(
|
||||||
|
config,
|
||||||
|
args,
|
||||||
|
&repo,
|
||||||
|
&raw_base,
|
||||||
|
&use_prebuilt,
|
||||||
|
binary_version.as_deref(),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
if let Some(local_installer_role) = local_installer_role {
|
||||||
run_update_installer(
|
run_update_installer(
|
||||||
config,
|
config,
|
||||||
&repo,
|
&repo,
|
||||||
&installer,
|
&installer,
|
||||||
installer_role,
|
local_installer_role,
|
||||||
&update_cache.display().to_string(),
|
&update_cache.display().to_string(),
|
||||||
&use_prebuilt,
|
&use_prebuilt,
|
||||||
binary_version.as_deref(),
|
binary_version.as_deref(),
|
||||||
)?;
|
)?;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4546,6 +4576,31 @@ fn run_update_installer(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_remote_server_update(
|
||||||
|
config: &dosh::config::ClientConfig,
|
||||||
|
args: &Args,
|
||||||
|
repo: &str,
|
||||||
|
raw_base: &str,
|
||||||
|
use_prebuilt: &str,
|
||||||
|
binary_version: Option<&str>,
|
||||||
|
) -> Result<()> {
|
||||||
|
anyhow::ensure!(
|
||||||
|
config.server != "user@example.com",
|
||||||
|
"set server in ~/.config/dosh/client.toml before running dosh update --server"
|
||||||
|
);
|
||||||
|
let installer = update_installer_url(raw_base, "linux");
|
||||||
|
let script =
|
||||||
|
remote_unix_server_update_script(config, repo, &installer, use_prebuilt, binary_version);
|
||||||
|
run_remote_script(
|
||||||
|
&config.server,
|
||||||
|
args.ssh_port.or(config.ssh_port),
|
||||||
|
args.ssh_key.as_deref(),
|
||||||
|
args.ssh_known_hosts.as_deref(),
|
||||||
|
&script,
|
||||||
|
"run remote Dosh server update",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn unix_update_script(
|
fn unix_update_script(
|
||||||
config: &dosh::config::ClientConfig,
|
config: &dosh::config::ClientConfig,
|
||||||
repo: &str,
|
repo: &str,
|
||||||
@@ -4580,6 +4635,29 @@ fn unix_update_script(
|
|||||||
script
|
script
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn remote_unix_server_update_script(
|
||||||
|
config: &dosh::config::ClientConfig,
|
||||||
|
repo: &str,
|
||||||
|
installer: &str,
|
||||||
|
use_prebuilt: &str,
|
||||||
|
binary_version: Option<&str>,
|
||||||
|
) -> String {
|
||||||
|
let mut env = format!(
|
||||||
|
"DOSH_REPO={} DOSH_PORT={} DOSH_UPDATE_QUIET=1 DOSH_USE_PREBUILT={}",
|
||||||
|
shell_word(repo),
|
||||||
|
config.update_port.unwrap_or(config.dosh_port),
|
||||||
|
shell_word(use_prebuilt)
|
||||||
|
);
|
||||||
|
if let Some(version) = binary_version {
|
||||||
|
env.push_str(&format!(" DOSH_BINARY_VERSION={}", shell_word(version)));
|
||||||
|
}
|
||||||
|
format!(
|
||||||
|
"curl -fsSL {} | {} sh -s -- server",
|
||||||
|
shell_word(installer),
|
||||||
|
env
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn windows_update_script(
|
fn windows_update_script(
|
||||||
config: &dosh::config::ClientConfig,
|
config: &dosh::config::ClientConfig,
|
||||||
repo: &str,
|
repo: &str,
|
||||||
@@ -10257,25 +10335,25 @@ mod tests {
|
|||||||
queue_or_send_stream_data, queue_pending_user_input, queue_stale_pending_user_input,
|
queue_or_send_stream_data, queue_pending_user_input, queue_stale_pending_user_input,
|
||||||
raw_contains_host_table, recv_response_until, refresh_live_addr, release_artifact_name_for,
|
raw_contains_host_table, recv_response_until, refresh_live_addr, release_artifact_name_for,
|
||||||
release_tag_download_url, release_tag_from_effective_url, release_version_from_tag,
|
release_tag_download_url, release_tag_from_effective_url, release_version_from_tag,
|
||||||
render_frame_bytes, render_status_clear, render_status_overlay, requested_env,
|
remote_unix_server_update_script, render_frame_bytes, render_status_clear,
|
||||||
resolve_forward_agent_endpoint, resolved_startup_command, retire_stream_state,
|
render_status_overlay, requested_env, resolve_forward_agent_endpoint,
|
||||||
retransmit_stream_closes, retransmit_stream_eofs, retransmit_stream_opens,
|
resolved_startup_command, retire_stream_state, retransmit_stream_closes,
|
||||||
retransmit_stream_window_adjusts, rewrite_forward_command, sanitize_trace_name,
|
retransmit_stream_eofs, retransmit_stream_opens, retransmit_stream_window_adjusts,
|
||||||
selected_predict_mode, selected_udp_host, send_stream_eof, server_version_mismatch,
|
rewrite_forward_command, sanitize_trace_name, selected_predict_mode, selected_udp_host,
|
||||||
should_flush_terminal_input_after_contact, should_health_log_client_start,
|
send_stream_eof, server_version_mismatch, should_flush_terminal_input_after_contact,
|
||||||
should_hold_during_startup_gate, should_hold_post_submit_input,
|
should_health_log_client_start, should_hold_during_startup_gate,
|
||||||
should_reconnect_before_input_for_local_sleep, should_repaint_idle_terminal,
|
should_hold_post_submit_input, should_reconnect_before_input_for_local_sleep,
|
||||||
should_strip_unowned_terminal_reports, split_after_command_submit, split_trace_tokens,
|
should_repaint_idle_terminal, should_strip_unowned_terminal_reports,
|
||||||
ssh_command_target, ssh_config_uses_proxy, ssh_config_word_for_os, ssh_destination_host,
|
split_after_command_submit, split_trace_tokens, ssh_command_target, ssh_config_uses_proxy,
|
||||||
ssh_username, ssh_with_user, startup_command, status_ssh_target, strip_stale_mouse_reports,
|
ssh_config_word_for_os, ssh_destination_host, ssh_username, ssh_with_user, startup_command,
|
||||||
strip_terminal_focus_reports, strip_unowned_terminal_reports, summarize_trace_file,
|
status_ssh_target, strip_stale_mouse_reports, strip_terminal_focus_reports,
|
||||||
summarize_trace_file_with_mode, terminal_private_mode_transition, toml_bare_key_or_quoted,
|
strip_unowned_terminal_reports, summarize_trace_file, summarize_trace_file_with_mode,
|
||||||
top_trace_events, trace_report_warnings, unix_update_script,
|
terminal_private_mode_transition, toml_bare_key_or_quoted, top_trace_events,
|
||||||
update_binary_version_for_installer, update_installer_url, update_version_status,
|
trace_report_warnings, unix_update_script, update_binary_version_for_installer,
|
||||||
upsert_managed_block, valid_forward_host, vscode_safe_alias, wake_repaint_retry_deadline,
|
update_installer_url, update_version_status, upsert_managed_block, valid_forward_host,
|
||||||
windows_command_word, windows_deferred_update_script, windows_effective_url_script,
|
vscode_safe_alias, wake_repaint_retry_deadline, windows_command_word,
|
||||||
windows_mode_from_readonly, windows_readonly_from_mode, windows_update_script,
|
windows_deferred_update_script, windows_effective_url_script, windows_mode_from_readonly,
|
||||||
windows_url_reachable_script,
|
windows_readonly_from_mode, windows_update_script, windows_url_reachable_script,
|
||||||
};
|
};
|
||||||
use dosh::config::{ClientConfig, CommandExtension, HostConfig};
|
use dosh::config::{ClientConfig, CommandExtension, HostConfig};
|
||||||
use dosh::native::EnvVar;
|
use dosh::native::EnvVar;
|
||||||
@@ -12461,21 +12539,31 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn windows_update_roles_are_client_only() {
|
fn windows_update_roles_split_local_client_and_remote_server() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
UpdateRole::Auto.installer_arg_for_os("windows").unwrap(),
|
UpdateRole::Auto.local_installer_arg_for_os("windows"),
|
||||||
"client"
|
Some("client")
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
UpdateRole::Client.installer_arg_for_os("windows").unwrap(),
|
UpdateRole::Client.local_installer_arg_for_os("windows"),
|
||||||
"client"
|
Some("client")
|
||||||
);
|
);
|
||||||
assert!(UpdateRole::Server.installer_arg_for_os("windows").is_err());
|
|
||||||
assert!(UpdateRole::Both.installer_arg_for_os("windows").is_err());
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
UpdateRole::Both.installer_arg_for_os("linux").unwrap(),
|
UpdateRole::Server.local_installer_arg_for_os("windows"),
|
||||||
"both"
|
None
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
UpdateRole::Both.local_installer_arg_for_os("windows"),
|
||||||
|
Some("client")
|
||||||
|
);
|
||||||
|
assert!(UpdateRole::Server.updates_remote_server_from_os("windows"));
|
||||||
|
assert!(UpdateRole::Both.updates_remote_server_from_os("windows"));
|
||||||
|
assert!(!UpdateRole::Client.updates_remote_server_from_os("windows"));
|
||||||
|
assert_eq!(
|
||||||
|
UpdateRole::Both.local_installer_arg_for_os("linux"),
|
||||||
|
Some("both")
|
||||||
|
);
|
||||||
|
assert!(!UpdateRole::Both.updates_remote_server_from_os("linux"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -12523,6 +12611,31 @@ mod tests {
|
|||||||
assert!(!windows.contains(" sh -s "));
|
assert!(!windows.contains(" sh -s "));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn windows_server_update_uses_remote_unix_installer_script() {
|
||||||
|
let config = ClientConfig {
|
||||||
|
server: "palav".to_string(),
|
||||||
|
dosh_port: 50000,
|
||||||
|
update_port: Some(50001),
|
||||||
|
..ClientConfig::default()
|
||||||
|
};
|
||||||
|
let script = remote_unix_server_update_script(
|
||||||
|
&config,
|
||||||
|
"https://git.palav.dev/Palav/dosh.git",
|
||||||
|
"https://git.palav.dev/Palav/dosh/raw/branch/main/install.sh",
|
||||||
|
"1",
|
||||||
|
Some("v1.0.0-rc41"),
|
||||||
|
);
|
||||||
|
assert!(script.contains("curl -fsSL"));
|
||||||
|
assert!(script.contains("install.sh"));
|
||||||
|
assert!(script.contains("DOSH_REPO='https://git.palav.dev/Palav/dosh.git'"));
|
||||||
|
assert!(script.contains("DOSH_PORT=50001"));
|
||||||
|
assert!(script.contains("DOSH_BINARY_VERSION='v1.0.0-rc41'"));
|
||||||
|
assert!(script.contains(" sh -s -- server"));
|
||||||
|
assert!(!script.contains("install.ps1"));
|
||||||
|
assert!(!script.contains("DOSH_ROLE='client'"));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn windows_update_script_waits_for_parent_before_replacing_locked_exe() {
|
fn windows_update_script_waits_for_parent_before_replacing_locked_exe() {
|
||||||
let installer = windows_update_script(
|
let installer = windows_update_script(
|
||||||
|
|||||||
Reference in New Issue
Block a user