Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
237ad52bef | ||
|
|
a8ba852f16 |
Generated
+1
-1
@@ -436,7 +436,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "dosh"
|
||||
version = "0.1.15"
|
||||
version = "0.1.17"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "dosh"
|
||||
version = "0.1.15"
|
||||
version = "0.1.17"
|
||||
edition = "2024"
|
||||
license = "MIT"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.PHONY: build test fmt clippy release-check install package-release package-release-linux package-release-windows publish-release bench-report bench-local bench-local-json bench-docker-ssh bench-docker-mosh fuzz-smoke fuzz-deep soak-local tui-harness
|
||||
.PHONY: build test fmt clippy release-check 1.0-check reconnect-check hostile-network-check persistence-check package-check install package-release package-release-linux package-release-windows publish-release bench-report bench-local bench-local-json bench-docker-ssh bench-docker-mosh fuzz-smoke fuzz-deep soak-local tui-harness
|
||||
|
||||
build:
|
||||
cargo build --release
|
||||
@@ -19,6 +19,24 @@ release-check:
|
||||
cargo test
|
||||
$(MAKE) tui-harness
|
||||
|
||||
1.0-check: release-check reconnect-check hostile-network-check persistence-check package-check
|
||||
|
||||
reconnect-check:
|
||||
cargo test --test integration_smoke resume_updates_udp_endpoint_for_roaming -- --nocapture
|
||||
DOSH_SOAK_SECONDS=$${DOSH_SOAK_SECONDS:-300} cargo test --test integration_smoke sleep_roaming_soak_30m -- --ignored --nocapture
|
||||
|
||||
hostile-network-check:
|
||||
cargo test --test hostile_network -- --nocapture
|
||||
|
||||
persistence-check:
|
||||
cargo test --test integration_smoke session_survives_server_restart_same_shell_and_screen -- --nocapture
|
||||
cargo test --test integration_smoke multiple_persistent_named_sessions_survive_restart_independently -- --nocapture
|
||||
|
||||
package-check:
|
||||
$(MAKE) package-release-linux
|
||||
$(MAKE) package-release-windows
|
||||
sh scripts/verify-release-artifacts.sh
|
||||
|
||||
install:
|
||||
sh install.sh --from-current
|
||||
|
||||
|
||||
+1
-1
@@ -426,7 +426,7 @@ Wants=network-online.target
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=$bindir/dosh-server serve
|
||||
Restart=on-failure
|
||||
Restart=always
|
||||
RestartSec=1
|
||||
KillMode=process
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ Wants=network-online.target
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=%h/.local/bin/dosh-server serve
|
||||
Restart=on-failure
|
||||
Restart=always
|
||||
RestartSec=1
|
||||
KillMode=process
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env sh
|
||||
set -eu
|
||||
|
||||
repo_root="$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)"
|
||||
cd "$repo_root"
|
||||
|
||||
version="$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | sed -n '1p')"
|
||||
out_dir="${DOSH_PACKAGE_DIR:-target/dosh-release}"
|
||||
|
||||
artifact_version() {
|
||||
artifact="$1"
|
||||
case "$artifact" in
|
||||
*.tar.gz)
|
||||
tar -xOf "$artifact" dosh/VERSION 2>/dev/null | tr -d '\r\n'
|
||||
;;
|
||||
*.zip)
|
||||
unzip -p "$artifact" dosh/VERSION 2>/dev/null | tr -d '\r\n'
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
failed=0
|
||||
for artifact in \
|
||||
"$out_dir/dosh-linux-x86_64.tar.gz" \
|
||||
"$out_dir/dosh-macos-aarch64.tar.gz" \
|
||||
"$out_dir/dosh-windows-x86_64.zip"; do
|
||||
if [ ! -f "$artifact" ]; then
|
||||
echo "missing release artifact: $artifact" >&2
|
||||
failed=1
|
||||
continue
|
||||
fi
|
||||
if [ ! -f "$artifact.sha256" ]; then
|
||||
echo "missing release checksum: $artifact.sha256" >&2
|
||||
failed=1
|
||||
fi
|
||||
actual="$(artifact_version "$artifact" || true)"
|
||||
if [ "$actual" != "$version" ]; then
|
||||
echo "artifact version mismatch: $artifact has ${actual:-unknown}, expected $version" >&2
|
||||
failed=1
|
||||
fi
|
||||
done
|
||||
|
||||
exit "$failed"
|
||||
+57
-5
@@ -4610,6 +4610,22 @@ async fn run_terminal(
|
||||
}
|
||||
PacketKind::Pong => {
|
||||
last_packet_at = Instant::now();
|
||||
if should_flush_terminal_input_after_contact(
|
||||
forward_only,
|
||||
&cred.mode,
|
||||
pending_user_input.is_empty(),
|
||||
) {
|
||||
flush_pending_user_input(
|
||||
&socket,
|
||||
addr,
|
||||
&cred,
|
||||
&mut send_seq,
|
||||
&mut predictor,
|
||||
&mut pending_user_input,
|
||||
&mut pending_user_input_bytes,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
PacketKind::Rekey => {
|
||||
// Server-initiated transport rekey (spec §11). The Rekey is
|
||||
@@ -5439,6 +5455,14 @@ fn should_hold_during_startup_gate(
|
||||
mode == StartupGateMode::HoldAll || already_queued || should_hold_post_submit_input(bytes)
|
||||
}
|
||||
|
||||
fn should_flush_terminal_input_after_contact(
|
||||
forward_only: bool,
|
||||
mode: &str,
|
||||
pending_empty: bool,
|
||||
) -> bool {
|
||||
!forward_only && !pending_empty && mode != "view-only" && mode != "forward-only"
|
||||
}
|
||||
|
||||
async fn flush_pending_user_input(
|
||||
socket: &UdpSocket,
|
||||
addr: SocketAddr,
|
||||
@@ -7095,11 +7119,11 @@ mod tests {
|
||||
release_tag_download_url, release_tag_from_effective_url, release_version_from_tag,
|
||||
render_status_clear, render_status_overlay, requested_env, resolved_startup_command,
|
||||
retransmit_stream_opens, rewrite_forward_command, selected_predict_mode, selected_udp_host,
|
||||
server_version_mismatch, should_hold_during_startup_gate, should_hold_post_submit_input,
|
||||
split_after_command_submit, ssh_destination_host, ssh_username, ssh_with_user,
|
||||
startup_command, status_ssh_target, strip_stale_mouse_reports, toml_bare_key_or_quoted,
|
||||
update_check_requested, update_version_status, upsert_managed_block, valid_forward_host,
|
||||
vscode_safe_alias,
|
||||
server_version_mismatch, should_flush_terminal_input_after_contact,
|
||||
should_hold_during_startup_gate, should_hold_post_submit_input, split_after_command_submit,
|
||||
ssh_destination_host, ssh_username, ssh_with_user, startup_command, status_ssh_target,
|
||||
strip_stale_mouse_reports, toml_bare_key_or_quoted, update_check_requested,
|
||||
update_version_status, upsert_managed_block, valid_forward_host, vscode_safe_alias,
|
||||
};
|
||||
use dosh::config::{ClientConfig, CommandExtension, HostConfig};
|
||||
use dosh::native::EnvVar;
|
||||
@@ -7879,6 +7903,34 @@ mod tests {
|
||||
assert_eq!(post_submit_hold_duration(b"x"), POST_SUBMIT_ALL_INPUT_HOLD);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn contact_flushes_pending_terminal_input_for_interactive_sessions() {
|
||||
assert!(should_flush_terminal_input_after_contact(
|
||||
false, "normal", false
|
||||
));
|
||||
assert!(should_flush_terminal_input_after_contact(
|
||||
false,
|
||||
"read-write",
|
||||
false
|
||||
));
|
||||
assert!(!should_flush_terminal_input_after_contact(
|
||||
false, "normal", true
|
||||
));
|
||||
assert!(!should_flush_terminal_input_after_contact(
|
||||
true, "normal", false
|
||||
));
|
||||
assert!(!should_flush_terminal_input_after_contact(
|
||||
false,
|
||||
"view-only",
|
||||
false
|
||||
));
|
||||
assert!(!should_flush_terminal_input_after_contact(
|
||||
false,
|
||||
"forward-only",
|
||||
false
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reconnect_refreshes_live_send_address_from_credentials() {
|
||||
let mut addr = "127.0.0.1:50000".parse().unwrap();
|
||||
|
||||
@@ -25,6 +25,10 @@ fn systemd_service_does_not_sandbox_remote_shells() {
|
||||
let install = include_str!("../install.sh");
|
||||
for raw in [service, install] {
|
||||
assert!(raw.contains("KillMode=process"));
|
||||
assert!(
|
||||
raw.contains("Restart=always"),
|
||||
"dosh-server should come back after accidental SIGTERM while preserving child shells"
|
||||
);
|
||||
for directive in [
|
||||
"NoNewPrivileges=",
|
||||
"PrivateTmp=",
|
||||
|
||||
Reference in New Issue
Block a user