fix: Fix installer retries for HTTP/2 download failures (#1910)

This commit is contained in:
chiehwangs
2026-05-16 20:17:52 -07:00
committed by GitHub
parent 0fcbe0a177
commit f0690aa981
4 changed files with 115 additions and 6 deletions
+2
View File
@@ -20,6 +20,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Unit tests (install.sh helpers)
run: bash scripts/test_install.sh
- name: Run installer dry-run
run: bash scripts/install.sh --dry-run --verbose
+4
View File
@@ -21,6 +21,10 @@ This is the **only** acceptable substitute for a `🚫` row in [`TEST-COVERAGE-M
Applies to every release, all platforms.
### Public installer script
- [ ] **`scripts/install.sh` downloads the latest asset on a proxy/VPN network** — From a clean checkout, run `bash scripts/install.sh --dry-run --verbose`, then run the public `curl -fsSL https://raw.githubusercontent.com/tinyhumansai/openhuman/main/scripts/install.sh | bash` flow on one macOS or Linux host. Expected: release metadata resolves, the asset downloads successfully, and transient GitHub/CDN HTTP/2 failures retry over HTTP/1.1 instead of surfacing `curl: (16) Error in the HTTP2 framing layer`.
### macOS
- [ ] **Gatekeeper accepts the signed `.app` on first launch** — Double-click the `.app` from a fresh download (Quarantine attribute set). Expected: app opens without `"OpenHuman" cannot be opened because the developer cannot be verified` dialog. If it appears, the build is unsigned or the notarization stapler is missing.
+42 -6
View File
@@ -200,11 +200,47 @@ PY
printf '%s\n' "$url"
}
# curl can fail on GitHub/CDN HTTP/2 framing issues on some networks while the
# same URL succeeds over HTTP/1.1. Try the normal path first, then a
# compatibility retry before surfacing the failure.
curl_get_file() {
local url="$1" output="$2" rc
if curl -fsSL "$url" -o "$output"; then
return 0
else
rc=$?
fi
log_warn "Request failed (curl rc=${rc}); retrying with HTTP/1.1."
curl --http1.1 -fsSL "$url" -o "$output"
}
curl_download_file() {
local url="$1" output="$2" rc
if curl -fL "$url" -o "$output"; then
return 0
else
rc=$?
fi
log_warn "Download failed (curl rc=${rc}); retrying with HTTP/1.1."
curl --http1.1 -fL "$url" -o "$output"
}
curl_head_with_http_fallback() {
local url="$1" rc
if curl -fsSI --max-time 10 "$url" >/dev/null 2>&1; then
return 0
else
rc=$?
fi
log_warn "Reachability check failed (curl rc=${rc}); retrying with HTTP/1.1."
curl --http1.1 -fsSI --max-time 10 "$url" >/dev/null 2>&1
}
# Retries an HTTP HEAD on the asset URL, fails loudly with the URL.
verify_asset_reachable() {
local url="$1" max_attempts=5 delay=2
for i in $(seq 1 $max_attempts); do
if curl -fsSI --max-time 10 "$url" >/dev/null 2>&1; then
if curl_head_with_http_fallback "$url"; then
return 0
fi
if [[ $i -lt $max_attempts ]]; then
@@ -217,7 +253,7 @@ verify_asset_reachable() {
}
resolve_from_latest_json() {
if ! curl -fsSL "${LATEST_JSON_URL}" -o "${LATEST_JSON_PATH}"; then
if ! curl_get_file "${LATEST_JSON_URL}" "${LATEST_JSON_PATH}"; then
return 1
fi
@@ -252,7 +288,7 @@ print(d.get('version', ''))
}
resolve_from_release_api() {
if ! curl -fsSL "${LATEST_RELEASE_API_URL}" -o "${RELEASE_JSON_PATH}"; then
if ! curl_get_file "${LATEST_RELEASE_API_URL}" "${RELEASE_JSON_PATH}"; then
return 1
fi
@@ -333,7 +369,7 @@ resolve_release_digest() {
return 0
fi
if [ ! -s "${RELEASE_JSON_PATH}" ]; then
if ! curl -fsSL "${LATEST_RELEASE_API_URL}" -o "${RELEASE_JSON_PATH}"; then
if ! curl_get_file "${LATEST_RELEASE_API_URL}" "${RELEASE_JSON_PATH}"; then
return 0
fi
fi
@@ -417,9 +453,9 @@ fi
DOWNLOAD_PATH="${TMP_DIR}/${ASSET_NAME}"
log_info "Downloading ${ASSET_NAME}"
if [ "${DRY_RUN}" = true ]; then
echo "DRY RUN: curl -fL ${ASSET_URL} -o ${DOWNLOAD_PATH}"
echo "DRY RUN: curl -fL ${ASSET_URL} -o ${DOWNLOAD_PATH} (retrying with --http1.1 on failure)"
else
curl -fL "${ASSET_URL}" -o "${DOWNLOAD_PATH}"
curl_download_file "${ASSET_URL}" "${DOWNLOAD_PATH}"
fi
compute_sha256() {
+67
View File
@@ -30,4 +30,71 @@ if [[ "$missing_platform_rc" -ne 3 ]]; then
exit 1
fi
assert_retry_shape() {
local calls="$1" label="$2"
local _ first second extra
IFS='|' read -r _ first second extra <<<"${calls}"
if [[ -z "${first:-}" || -z "${second:-}" || -n "${extra:-}" ]]; then
echo "FAIL: ${label} should issue exactly 2 curl calls (base + HTTP/1.1 retry)"
exit 1
fi
if [[ "${first}" == *"--http1.1"* || "${second}" != *"--http1.1"* ]]; then
echo "FAIL: ${label} should retry with --http1.1 only on the second call"
exit 1
fi
}
(
CURL_CALLS=""
curl() {
CURL_CALLS="${CURL_CALLS}|$*"
case " $* " in
*" --http1.1 "*) return 0 ;;
*) return 16 ;;
esac
}
if ! curl_head_with_http_fallback "https://example.invalid/OpenHuman.app.tar.gz"; then
echo "FAIL: reachability fallback should succeed when HTTP/1.1 retry succeeds"
exit 1
fi
assert_retry_shape "${CURL_CALLS}" "reachability check"
)
(
CURL_CALLS=""
curl() {
CURL_CALLS="${CURL_CALLS}|$*"
case " $* " in
*" --http1.1 "*) return 0 ;;
*) return 16 ;;
esac
}
if ! curl_get_file "https://example.invalid/latest.json" "/tmp/openhuman-test-latest.json"; then
echo "FAIL: metadata fetch fallback should succeed when HTTP/1.1 retry succeeds"
exit 1
fi
assert_retry_shape "${CURL_CALLS}" "metadata fetch"
)
(
CURL_CALLS=""
curl() {
CURL_CALLS="${CURL_CALLS}|$*"
case " $* " in
*" --http1.1 "*) return 0 ;;
*) return 16 ;;
esac
}
if ! curl_download_file "https://example.invalid/OpenHuman.app.tar.gz" "/tmp/openhuman-test-download"; then
echo "FAIL: download fallback should succeed when HTTP/1.1 retry succeeds"
exit 1
fi
assert_retry_shape "${CURL_CALLS}" "download"
)
echo "PASS"