Unify release selection across clients
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-17 18:58:58 -04:00
parent 89b81d73b1
commit 7f8d5711ea
7 changed files with 641 additions and 439 deletions
+38 -122
View File
@@ -17,6 +17,7 @@ binary_url="${DOSH_BINARY_URL:-}"
binary_base="${DOSH_BINARY_BASE:-}"
binary_name="${DOSH_BINARY_NAME:-}"
binary_version="${DOSH_BINARY_VERSION:-latest}"
binary_exact="${DOSH_BINARY_EXACT:-0}"
binary_required="${DOSH_BINARY_REQUIRED:-0}"
usage() {
@@ -47,7 +48,9 @@ Environment alternatives:
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
Requested release tag; repository version wins unless exact
DOSH_BINARY_EXACT=1
Require DOSH_BINARY_VERSION exactly, including downgrades
DOSH_BINARY_REQUIRED=1
Fail instead of falling back to source when binary install fails
EOF
@@ -173,6 +176,7 @@ config_dir="$HOME/.config/dosh"
data_dir="$HOME/.local/share/dosh"
systemd_user_dir="$HOME/.config/systemd/user"
src_dir=""
resolved_source_ref=""
mkdir -p "$bindir" "$config_dir" "$data_dir"
@@ -228,97 +232,13 @@ release_download_url() {
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
}
release_latest_tag_download_url() {
if [ -n "$binary_url" ] || [ -n "$binary_base" ] || [ "$binary_version" != "latest" ] || [ -z "$repo" ]; then
return 1
fi
web_base="$(repo_web_base "$repo")" || return 1
latest_url="$(curl -fsSL -o /dev/null -w '%{url_effective}' "$web_base/releases/latest" 2>/dev/null || true)"
case "$latest_url" in
"$web_base"/releases/tag/*)
tag="${latest_url##"$web_base"/releases/tag/}"
[ -n "$tag" ] || return 1
printf '%s/releases/download/%s/%s\n' "$web_base" "$tag" "$(release_artifact_name)"
;;
*)
return 1
;;
esac
}
version_core() {
value="${1#v}"
value="${value%%+*}"
printf '%s\n' "${value%%-*}"
}
version_prerelease() {
value="${1#v}"
value="${value%%+*}"
case "$value" in
*-*) printf '%s\n' "${value#*-}" ;;
*) printf '\n' ;;
esac
}
version_core_part() {
version="$1"
index="$2"
version_core "$version" \
| sed 's/[^0-9][^0-9]*/ /g' \
| awk -v index="$index" '{ value=$index; if (value == "") value=0; print value }'
}
prerelease_less_than() {
left="$1"
right="$2"
if [ -z "$left" ] && [ -z "$right" ]; then
return 1
fi
if [ -z "$left" ]; then
return 1
fi
if [ -z "$right" ]; then
return 0
fi
if [ "$left" = "$right" ]; then
return 1
fi
left_rc="$(printf '%s\n' "$left" | sed -n 's/^[Rr][Cc]\([0-9][0-9]*\)$/\1/p')"
right_rc="$(printf '%s\n' "$right" | sed -n 's/^[Rr][Cc]\([0-9][0-9]*\)$/\1/p')"
if [ -n "$left_rc" ] && [ -n "$right_rc" ]; then
[ "$left_rc" -lt "$right_rc" ]
return
fi
first="$(printf '%s\n%s\n' "$left" "$right" | LC_ALL=C sort | sed -n '1p')"
[ "$first" = "$left" ]
}
version_less_than() {
left="$1"
right="$2"
for index in 1 2 3 4 5 6 7 8; do
lpart="$(version_core_part "$left" "$index")"
rpart="$(version_core_part "$right" "$index")"
if [ "$lpart" -lt "$rpart" ]; then
return 0
fi
if [ "$lpart" -gt "$rpart" ]; then
return 1
fi
done
prerelease_less_than "$(version_prerelease "$left")" "$(version_prerelease "$right")"
release_ref="$(release_source_ref)" || return 1
printf '%s/releases/download/%s/%s\n' "$web_base" "$release_ref" "$(release_artifact_name)"
}
current_source_version() {
if [ -f Cargo.toml ]; then
if [ "$from_current" -eq 1 ]; then
[ -f Cargo.toml ] || return 1
sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | sed -n '1p'
return 0
fi
@@ -329,32 +249,21 @@ current_source_version() {
| sed -n '1p'
}
latest_release_tag() {
[ -n "$repo" ] || return 1
web_base="$(repo_web_base "$repo")" || return 1
latest_url="$(curl -fsSL -o /dev/null -w '%{url_effective}' "$web_base/releases/latest" 2>/dev/null || true)"
case "$latest_url" in
"$web_base"/releases/tag/*)
tag="${latest_url##"$web_base"/releases/tag/}"
[ -n "$tag" ] || return 1
printf '%s\n' "$tag"
;;
*)
return 1
;;
esac
}
latest_release_is_stale() {
if [ -n "$binary_url" ] || [ -n "$binary_base" ] || [ "$binary_version" != "latest" ]; then
release_source_ref() {
if [ -n "$binary_url" ] || [ -n "$binary_base" ]; then
return 1
fi
latest="$(latest_release_tag || true)"
if [ "$binary_version" != "latest" ] && [ "$binary_exact" = "1" ]; then
printf '%s\n' "$binary_version"
return 0
fi
current="$(current_source_version || true)"
[ -n "$latest" ] && [ -n "$current" ] || return 1
latest="${latest#v}"
if version_less_than "$latest" "$current"; then
echo "latest release $latest is older than source $current; skipping stale prebuilt" >&2
if [ -n "$current" ]; then
printf 'v%s\n' "${current#v}"
return 0
fi
if [ "$binary_version" != "latest" ]; then
printf '%s\n' "$binary_version"
return 0
fi
return 1
@@ -450,10 +359,8 @@ verify_archive_version() {
}
try_install_prebuilt() {
if latest_release_is_stale; then
return 1
fi
download_url="$(release_latest_tag_download_url || release_download_url)" || return 1
download_url="$(release_download_url)" || return 1
resolved_source_ref="$(release_source_ref || true)"
tmpdir="$(mktemp -d)"
archive="$tmpdir/$(release_artifact_name)"
checksum_file="$archive.sha256"
@@ -490,7 +397,11 @@ try_install_prebuilt() {
install_from_source() {
ensure_cargo
if [ "$from_current" -eq 1 ] || [ -f Cargo.toml ]; then
if [ "$from_current" -eq 1 ]; then
[ -f Cargo.toml ] || {
echo "--from-current requires a Dosh checkout with Cargo.toml" >&2
exit 2
}
src_dir="$(pwd)"
else
if [ -z "$repo" ]; then
@@ -500,22 +411,27 @@ install_from_source() {
update_cache="$(safe_update_cache_path "$update_cache")"
need git
mkdir -p "$(dirname "$update_cache")"
source_ref="${resolved_source_ref:-main}"
if [ -d "$update_cache/.git" ]; then
[ "$quiet" != "1" ] && echo "Updating Dosh source"
git -C "$update_cache" remote set-url origin "$repo"
if [ "$quiet" = "1" ]; then
git -C "$update_cache" fetch -q --depth 1 origin main
git -C "$update_cache" fetch -q --depth 1 origin "$source_ref"
else
git -C "$update_cache" fetch --depth 1 origin main
git -C "$update_cache" fetch --depth 1 origin "$source_ref"
fi
if [ "$source_ref" = "main" ]; then
git -C "$update_cache" checkout -q -B main FETCH_HEAD
else
git -C "$update_cache" checkout -q --detach FETCH_HEAD
fi
git -C "$update_cache" checkout -q -B main FETCH_HEAD
else
[ "$quiet" != "1" ] && echo "Downloading Dosh source"
rm -rf "$update_cache"
if [ "$quiet" = "1" ]; then
git clone -q --depth 1 --branch main "$repo" "$update_cache" >/dev/null
git clone -q --depth 1 --branch "$source_ref" "$repo" "$update_cache" >/dev/null
else
git clone --depth 1 --branch main "$repo" "$update_cache" >/dev/null
git clone --depth 1 --branch "$source_ref" "$repo" "$update_cache" >/dev/null
fi
fi
src_dir="$update_cache"