Add prebuilt update path

This commit is contained in:
DuProcess
2026-06-19 16:40:58 -04:00
parent 774da7371e
commit 7884ea2796
8 changed files with 297 additions and 74 deletions
+4 -1
View File
@@ -1,4 +1,4 @@
.PHONY: build test fmt install bench-local bench-local-json bench-docker-ssh bench-docker-mosh fuzz-smoke fuzz-deep soak-local .PHONY: build test fmt install package-release bench-local bench-local-json bench-docker-ssh bench-docker-mosh fuzz-smoke fuzz-deep soak-local
build: build:
cargo build --release cargo build --release
@@ -12,6 +12,9 @@ fmt:
install: install:
sh packaging/install.sh sh packaging/install.sh
package-release:
sh scripts/package-release.sh
# Safe, self-contained local benchmark matrix (native cold auth, cached attach # Safe, self-contained local benchmark matrix (native cold auth, cached attach
# ticket, local-auth) on a throwaway server bound to 127.0.0.1 on a free port in # ticket, local-auth) on a throwaway server bound to 127.0.0.1 on a free port in
# a temp HOME. Never touches the production server or UDP port 50000. # a temp HOME. Never touches the production server or UDP port 50000.
+11
View File
@@ -119,6 +119,11 @@ Update an installed client later:
dosh update dosh update
``` ```
`dosh update` first tries a release tarball named for the platform
(`dosh-macos-aarch64.tar.gz`, `dosh-linux-x86_64.tar.gz`, etc.) from the latest
Gitea/GitHub release. If that asset is not published yet, it falls back to the
source build path.
Install the client on Windows PowerShell: Install the client on Windows PowerShell:
```powershell ```powershell
@@ -266,6 +271,12 @@ Install release binaries and the user systemd service:
make install make install
``` ```
Build release tarballs for upload to Gitea/GitHub releases:
```bash
make package-release
```
## Performance Rules ## Performance Rules
The stack is performance-driven, not fixed by taste. Rust is the default because the The stack is performance-driven, not fixed by taste. Rust is the default because the
+2
View File
@@ -468,6 +468,8 @@ dosh_port = 50000
default_session = "new" default_session = "new"
reconnect_timeout_secs = 5 reconnect_timeout_secs = 5
view_only = false view_only = false
predict = true
predict_mode = "experimental"
cache_attach_tickets = true cache_attach_tickets = true
credential_cache = "~/.local/share/dosh/credentials" credential_cache = "~/.local/share/dosh/credentials"
``` ```
+183 -27
View File
@@ -12,6 +12,12 @@ start_server=1
force_config=0 force_config=0
update_cache="${DOSH_UPDATE_CACHE:-$HOME/.cache/dosh/source}" update_cache="${DOSH_UPDATE_CACHE:-$HOME/.cache/dosh/source}"
quiet="${DOSH_UPDATE_QUIET:-0}" quiet="${DOSH_UPDATE_QUIET:-0}"
use_prebuilt="${DOSH_USE_PREBUILT:-0}"
binary_url="${DOSH_BINARY_URL:-}"
binary_base="${DOSH_BINARY_BASE:-}"
binary_name="${DOSH_BINARY_NAME:-}"
binary_version="${DOSH_BINARY_VERSION:-latest}"
binary_required="${DOSH_BINARY_REQUIRED:-0}"
usage() { usage() {
cat <<'EOF' cat <<'EOF'
@@ -32,6 +38,18 @@ Options:
Environment alternatives: Environment alternatives:
DOSH_REPO, DOSH_ROLE, DOSH_SERVER, DOSH_HOST, DOSH_PORT, PREFIX, DOSH_REPO, DOSH_ROLE, DOSH_SERVER, DOSH_HOST, DOSH_PORT, PREFIX,
DOSH_UPDATE_CACHE DOSH_UPDATE_CACHE
DOSH_USE_PREBUILT=1
Try a release tarball before building from source
DOSH_BINARY_URL URL
Exact release tarball URL to install
DOSH_BINARY_BASE URL
Release download base; defaults to REPO/releases/latest/download
DOSH_BINARY_NAME NAME
Release tarball name; defaults to dosh-OS-ARCH.tar.gz
DOSH_BINARY_VERSION TAG
Release tag when deriving DOSH_BINARY_BASE; default latest
DOSH_BINARY_REQUIRED=1
Fail instead of falling back to source when binary install fails
EOF EOF
} }
@@ -112,8 +130,6 @@ ensure_cargo() {
. "$HOME/.cargo/env" . "$HOME/.cargo/env"
} }
ensure_cargo
cleanup() { cleanup() {
if [ -n "${tmpdir:-}" ]; then if [ -n "${tmpdir:-}" ]; then
rm -rf "$tmpdir" rm -rf "$tmpdir"
@@ -121,9 +137,119 @@ cleanup() {
} }
trap cleanup EXIT INT TERM trap cleanup EXIT INT TERM
if [ "$from_current" -eq 1 ] || [ -f Cargo.toml ]; then bindir="$prefix/bin"
config_dir="$HOME/.config/dosh"
data_dir="$HOME/.local/share/dosh"
systemd_user_dir="$HOME/.config/systemd/user"
src_dir=""
mkdir -p "$bindir" "$config_dir" "$data_dir"
normalize_os() {
case "$(uname -s)" in
Darwin) printf '%s\n' macos ;;
Linux) printf '%s\n' linux ;;
FreeBSD) printf '%s\n' freebsd ;;
*) uname -s | tr '[:upper:]' '[:lower:]' ;;
esac
}
normalize_arch() {
case "$(uname -m)" in
x86_64|amd64) printf '%s\n' x86_64 ;;
arm64|aarch64) printf '%s\n' aarch64 ;;
armv7l) printf '%s\n' armv7 ;;
*) uname -m | tr '[:upper:]' '[:lower:]' ;;
esac
}
repo_web_base() {
repo_base="$1"
case "$repo_base" in
http://*|https://*)
repo_base="${repo_base%.git}"
printf '%s\n' "${repo_base%/}"
;;
*)
return 1
;;
esac
}
release_artifact_name() {
if [ -n "$binary_name" ]; then
printf '%s\n' "$binary_name"
else
printf 'dosh-%s-%s.tar.gz\n' "$(normalize_os)" "$(normalize_arch)"
fi
}
release_download_url() {
if [ -n "$binary_url" ]; then
printf '%s\n' "$binary_url"
return 0
fi
if [ -n "$binary_base" ]; then
printf '%s/%s\n' "${binary_base%/}" "$(release_artifact_name)"
return 0
fi
if [ -z "$repo" ]; then
return 1
fi
web_base="$(repo_web_base "$repo")" || return 1
if [ "$binary_version" = "latest" ]; then
printf '%s/releases/latest/download/%s\n' "$web_base" "$(release_artifact_name)"
else
printf '%s/releases/download/%s/%s\n' "$web_base" "$binary_version" "$(release_artifact_name)"
fi
}
find_extracted_binary() {
find "$1" -type f -name "$2" 2>/dev/null | sed -n '1p'
}
install_extracted_binary() {
found="$(find_extracted_binary "$1" "$2")"
if [ -z "$found" ]; then
echo "prebuilt archive missing $2" >&2
return 1
fi
install -m 0755 "$found" "$3"
}
try_install_prebuilt() {
download_url="$(release_download_url)" || return 1
tmpdir="$(mktemp -d)"
archive="$tmpdir/$(release_artifact_name)"
[ "$quiet" = "1" ] && echo "Trying Dosh prebuilt $(release_artifact_name)"
need curl
need tar
if ! curl -fL "$download_url" -o "$archive"; then
echo "prebuilt unavailable: $download_url" >&2
return 1
fi
mkdir -p "$tmpdir/extract"
if ! tar -xzf "$archive" -C "$tmpdir/extract"; then
echo "prebuilt archive could not be extracted: $download_url" >&2
return 1
fi
install_extracted_binary "$tmpdir/extract" dosh-client "$bindir/dosh-client" || return 1
ln -sf dosh-client "$bindir/dosh"
if [ "$role" = "server" ] || [ "$role" = "both" ]; then
install_extracted_binary "$tmpdir/extract" dosh-server "$bindir/dosh-server" || return 1
install_extracted_binary "$tmpdir/extract" dosh-auth "$bindir/dosh-auth" || return 1
fi
if found_bench="$(find_extracted_binary "$tmpdir/extract" dosh-bench)" && [ -n "$found_bench" ]; then
install -m 0755 "$found_bench" "$bindir/dosh-bench"
fi
return 0
}
install_from_source() {
ensure_cargo
if [ "$from_current" -eq 1 ] || [ -f Cargo.toml ]; then
src_dir="$(pwd)" src_dir="$(pwd)"
else else
if [ -z "$repo" ]; then if [ -z "$repo" ]; then
echo "DOSH_REPO or --repo is required when running the installer from curl" >&2 echo "DOSH_REPO or --repo is required when running the installer from curl" >&2
exit 2 exit 2
@@ -155,39 +281,69 @@ else
fi fi
fi fi
src_dir="$update_cache" src_dir="$update_cache"
fi fi
cd "$src_dir" cd "$src_dir"
[ "$quiet" = "1" ] && echo "Building Dosh $role" [ "$quiet" = "1" ] && echo "Building Dosh $role"
if [ "$role" = "client" ]; then if [ "$role" = "client" ]; then
if [ "$quiet" = "1" ]; then if [ "$quiet" = "1" ]; then
cargo build -q --release --bin dosh-client cargo build -q --release --bin dosh-client
else else
cargo build --release --bin dosh-client cargo build --release --bin dosh-client
fi fi
else else
if [ "$quiet" = "1" ]; then if [ "$quiet" = "1" ]; then
cargo build -q --release cargo build -q --release
else else
cargo build --release cargo build --release
fi fi
fi fi
bindir="$prefix/bin" install -m 0755 target/release/dosh-client "$bindir/dosh-client"
config_dir="$HOME/.config/dosh" ln -sf dosh-client "$bindir/dosh"
data_dir="$HOME/.local/share/dosh" if [ "$role" = "server" ] || [ "$role" = "both" ]; then
systemd_user_dir="$HOME/.config/systemd/user"
mkdir -p "$bindir" "$config_dir" "$data_dir"
install -m 0755 target/release/dosh-client "$bindir/dosh-client"
ln -sf dosh-client "$bindir/dosh"
if [ "$role" = "server" ] || [ "$role" = "both" ]; then
install -m 0755 target/release/dosh-server "$bindir/dosh-server" install -m 0755 target/release/dosh-server "$bindir/dosh-server"
install -m 0755 target/release/dosh-auth "$bindir/dosh-auth" install -m 0755 target/release/dosh-auth "$bindir/dosh-auth"
fi fi
if [ -f target/release/dosh-bench ]; then if [ -f target/release/dosh-bench ]; then
install -m 0755 target/release/dosh-bench "$bindir/dosh-bench" install -m 0755 target/release/dosh-bench "$bindir/dosh-bench"
fi
}
write_systemd_service() {
if [ -n "$src_dir" ] && [ -f "$src_dir/packaging/systemd/dosh-server.service" ]; then
sed "s#ExecStart=%h/.local/bin/dosh-server serve#ExecStart=$bindir/dosh-server serve#" \
"$src_dir/packaging/systemd/dosh-server.service" >"$systemd_user_dir/dosh-server.service"
else
cat >"$systemd_user_dir/dosh-server.service" <<EOF
[Unit]
Description=Dosh server
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=$bindir/dosh-server serve
Restart=on-failure
RestartSec=1
KillMode=process
[Install]
WantedBy=default.target
EOF
fi
}
if [ "$use_prebuilt" != "0" ]; then
if ! try_install_prebuilt; then
if [ "$binary_required" = "1" ]; then
exit 1
fi
[ "$quiet" = "1" ] && echo "Falling back to source build"
install_from_source
fi
else
install_from_source
fi fi
if [ "$role" = "server" ] || [ "$role" = "both" ]; then if [ "$role" = "server" ] || [ "$role" = "both" ]; then
@@ -226,8 +382,7 @@ EOF
if command -v systemctl >/dev/null 2>&1; then if command -v systemctl >/dev/null 2>&1; then
mkdir -p "$systemd_user_dir" mkdir -p "$systemd_user_dir"
sed "s#ExecStart=%h/.local/bin/dosh-server serve#ExecStart=$bindir/dosh-server serve#" \ write_systemd_service
packaging/systemd/dosh-server.service >"$systemd_user_dir/dosh-server.service"
if [ "$start_server" -eq 1 ] && systemctl --user daemon-reload >/dev/null 2>&1; then if [ "$start_server" -eq 1 ] && systemctl --user daemon-reload >/dev/null 2>&1; then
systemctl --user enable --now dosh-server.service systemctl --user enable --now dosh-server.service
fi fi
@@ -272,7 +427,8 @@ dosh_port = $port
default_session = "new" default_session = "new"
reconnect_timeout_secs = 5 reconnect_timeout_secs = 5
view_only = false view_only = false
predict = false predict = true
predict_mode = "experimental"
cache_attach_tickets = true cache_attach_tickets = true
credential_cache = "~/.local/share/dosh/credentials" credential_cache = "~/.local/share/dosh/credentials"
auth_preference = "native,ssh" auth_preference = "native,ssh"
@@ -300,13 +456,13 @@ EOF
# dosh_host = "palav.dev" # dosh_host = "palav.dev"
# port = 50000 # port = 50000
# default_command = "tm" # default_command = "tm"
# predict = false # predict = true
[default] [default]
ssh = "$default_server" ssh = "$default_server"
dosh_host = "$host_udp" dosh_host = "$host_udp"
port = $port port = $port
predict = false predict = true
EOF EOF
fi fi
fi fi
+52
View File
@@ -0,0 +1,52 @@
#!/usr/bin/env sh
set -eu
repo_root="$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)"
cd "$repo_root"
out_dir="${DOSH_PACKAGE_DIR:-target/dosh-release}"
version="${DOSH_VERSION:-$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | sed -n '1p')}"
normalize_os() {
case "$(uname -s)" in
Darwin) printf '%s\n' macos ;;
Linux) printf '%s\n' linux ;;
FreeBSD) printf '%s\n' freebsd ;;
*) uname -s | tr '[:upper:]' '[:lower:]' ;;
esac
}
normalize_arch() {
case "$(uname -m)" in
x86_64|amd64) printf '%s\n' x86_64 ;;
arm64|aarch64) printf '%s\n' aarch64 ;;
armv7l) printf '%s\n' armv7 ;;
*) uname -m | tr '[:upper:]' '[:lower:]' ;;
esac
}
os="$(normalize_os)"
arch="$(normalize_arch)"
artifact="dosh-$os-$arch.tar.gz"
versioned_artifact="dosh-$version-$os-$arch.tar.gz"
stage="$out_dir/stage/dosh"
cargo build --release
rm -rf "$stage"
mkdir -p "$stage/bin" "$out_dir"
for bin in dosh-client dosh-server dosh-auth dosh-bench; do
if [ -f "target/release/$bin" ]; then
install -m 0755 "target/release/$bin" "$stage/bin/$bin"
fi
done
printf '%s\n' "$version" >"$stage/VERSION"
tar -C "$out_dir/stage" -czf "$out_dir/$artifact" dosh
cp "$out_dir/$artifact" "$out_dir/$versioned_artifact"
cat <<EOF
Wrote:
$out_dir/$artifact
$out_dir/$versioned_artifact
EOF
+2 -2
View File
@@ -1091,7 +1091,7 @@ fn run_update(config: &dosh::config::ClientConfig) -> Result<()> {
.join("dosh") .join("dosh")
.join("source"); .join("source");
let mut script = format!( let mut script = format!(
"curl -fsSL {} | DOSH_REPO={} DOSH_PORT={} DOSH_UPDATE_CACHE={} DOSH_UPDATE_QUIET=1 sh -s -- client", "curl -fsSL {} | DOSH_REPO={} DOSH_PORT={} DOSH_UPDATE_CACHE={} DOSH_UPDATE_QUIET=1 DOSH_USE_PREBUILT=1 sh -s -- client",
shell_word(&installer), shell_word(&installer),
shell_word(&repo), shell_word(&repo),
config.update_port.unwrap_or(config.dosh_port), config.update_port.unwrap_or(config.dosh_port),
@@ -1152,7 +1152,7 @@ fn run_import_ssh(config: &dosh::config::ClientConfig, aliases: &[String]) -> Re
{ {
raw.push_str(&format!("ssh_port = {port}\n")); raw.push_str(&format!("ssh_port = {port}\n"));
} }
raw.push_str("predict = false\n"); raw.push_str("predict = true\n");
} }
if let Some(parent) = path.parent() { if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?; fs::create_dir_all(parent)?;
+2 -2
View File
@@ -104,7 +104,7 @@ pub struct ClientConfig {
pub default_session: String, pub default_session: String,
pub reconnect_timeout_secs: u64, pub reconnect_timeout_secs: u64,
pub view_only: bool, pub view_only: bool,
#[serde(default)] #[serde(default = "default_true")]
pub predict: bool, pub predict: bool,
/// Prediction display policy: "off", "experimental" (adaptive, the default), /// Prediction display policy: "off", "experimental" (adaptive, the default),
/// or "always". Controls when speculative local echo is shown. /// or "always". Controls when speculative local echo is shown.
@@ -193,7 +193,7 @@ impl Default for ClientConfig {
default_session: "new".to_string(), default_session: "new".to_string(),
reconnect_timeout_secs: 5, reconnect_timeout_secs: 5,
view_only: false, view_only: false,
predict: false, predict: true,
predict_mode: default_predict_mode(), predict_mode: default_predict_mode(),
cache_attach_tickets: true, cache_attach_tickets: true,
credential_cache: "~/.local/share/dosh/credentials".to_string(), credential_cache: "~/.local/share/dosh/credentials".to_string(),
+1 -2
View File
@@ -1407,8 +1407,7 @@ fn large_tui_paint_is_delivered_in_mtu_safe_frames() {
let mut text = String::new(); let mut text = String::new();
let mut saw_split_frame = false; let mut saw_split_frame = false;
let deadline = std::time::Instant::now() + Duration::from_secs(3); let deadline = std::time::Instant::now() + Duration::from_secs(3);
while std::time::Instant::now() < deadline && text.matches("DOSH_TUI_BIG_PAINT").count() < 200 while std::time::Instant::now() < deadline && text.matches("DOSH_TUI_BIG_PAINT").count() < 200 {
{
if let Some((_header, frame)) = recv_frame(&socket, &bootstrap.session_key) { if let Some((_header, frame)) = recv_frame(&socket, &bootstrap.session_key) {
if frame.bytes.len() <= 1400 && frame.bytes.len() >= 900 { if frame.bytes.len() <= 1400 && frame.bytes.len() >= 900 {
saw_split_frame = true; saw_split_frame = true;