Skip to content

Commit dcaab04

Browse files
mdesmetclaude
authored andcommitted
fix(install): don't hard-fail when the GitHub releases API blips
Reported on #930: a transient 504 from api.github.com/.../releases/latest (or the 60/hr/IP unauthenticated rate limit) aborted the whole install with "Failed to fetch version information" — even though the download itself uses releases/latest/download/<file>, which GitHub resolves server-side with no API call. The API response only feeds the version-string display and the already-installed short-circuit. Both installers now, in the latest path: - retry the API call up to 3x with linear backoff (bash uses curl --fail so a 504 retries instead of parsing an error body); - on continued failure, print a muted notice and proceed to install latest anyway (version string shown as "latest"); - only short-circuit as "already installed" on a real version match — never treat empty==empty (unresolved version + unreadable binary) as installed. Pinned-version installs (-Version / --version) are unchanged: a genuine 404 still hard-fails. Tests: version-fetch-resilience.test.ts pins the retry + graceful-degrade behavior in both installers. bash -n clean; install.ps1 parses clean and the Pester suite (6/6) still passes on PowerShell 7.6.2. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent f8b3454 commit dcaab04

3 files changed

Lines changed: 82 additions & 18 deletions

File tree

install

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -205,11 +205,20 @@ else
205205

206206
if [ -z "$requested_version" ]; then
207207
url="https://github.com/AltimateAI/altimate-code/releases/latest/download/$filename"
208-
specific_version=$(curl -s https://api.github.com/repos/AltimateAI/altimate-code/releases/latest | sed -n 's/.*"tag_name": *"v\([^"]*\)".*/\1/p')
209-
210-
if [[ $? -ne 0 || -z "$specific_version" ]]; then
211-
echo -e "${RED}Failed to fetch version information${NC}"
212-
exit 1
208+
# The download above resolves "latest" server-side, so this API call only
209+
# feeds the version display and the already-installed short-circuit. A
210+
# transient api.github.com blip or the unauthenticated rate limit
211+
# (60/hr/IP) must NOT abort the install — retry a few times with --fail
212+
# (so a 504 retries instead of parsing an error body), then proceed
213+
# without the version string.
214+
specific_version=""
215+
for attempt in 1 2 3; do
216+
specific_version=$(curl -fsSL https://api.github.com/repos/AltimateAI/altimate-code/releases/latest 2>/dev/null | sed -n 's/.*"tag_name": *"v\([^"]*\)".*/\1/p')
217+
[ -n "$specific_version" ] && break
218+
[ "$attempt" -lt 3 ] && sleep "$attempt"
219+
done
220+
if [ -z "$specific_version" ]; then
221+
echo -e "${MUTED}Could not resolve the latest version from GitHub (API unavailable) — installing the latest release anyway.${NC}"
213222
fi
214223
else
215224
# Strip leading 'v' if present
@@ -255,11 +264,14 @@ check_version() {
255264
if [ -n "$probe" ]; then
256265
installed_version=$("$probe" --version 2>/dev/null || echo "")
257266

258-
if [[ "$installed_version" != "$specific_version" ]]; then
259-
print_message info "${MUTED}Installed version: ${NC}$installed_version."
260-
else
267+
# Only short-circuit on a real version match. When the latest version
268+
# couldn't be resolved (API unavailable → specific_version empty), never
269+
# treat an empty==empty as "already installed" — fall through and reinstall.
270+
if [ -n "$specific_version" ] && [[ "$installed_version" == "$specific_version" ]]; then
261271
print_message info "${MUTED}Version ${NC}$specific_version${MUTED} already installed${NC}"
262272
exit 0
273+
elif [ -n "$installed_version" ]; then
274+
print_message info "${MUTED}Installed version: ${NC}$installed_version."
263275
fi
264276
fi
265277
}
@@ -357,7 +369,7 @@ download_with_progress() {
357369
}
358370

359371
download_and_install() {
360-
print_message info "\n${MUTED}Installing ${NC}altimate ${MUTED}version: ${NC}$specific_version"
372+
print_message info "\n${MUTED}Installing ${NC}altimate ${MUTED}version: ${NC}${specific_version:-latest}"
361373
local tmp_dir="${TMPDIR:-/tmp}/altimate_install_$$"
362374
mkdir -p "$tmp_dir"
363375

install.ps1

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -112,16 +112,22 @@ function Test-Avx2 {
112112
# ---------------------------------------------------------------------------
113113
if ([string]::IsNullOrWhiteSpace($Version)) {
114114
$useLatest = $true
115-
try {
116-
$rel = Invoke-RestMethod -Uri "https://api.github.com/repos/AltimateAI/altimate-code/releases/latest" -Headers @{ "User-Agent" = "altimate-install" }
117-
$specificVersion = ($rel.tag_name -replace '^v', '')
118-
} catch {
119-
Write-Err "Failed to fetch version information"
120-
exit 1
115+
# The download below resolves "latest" server-side (releases/latest/download),
116+
# so this API call only feeds the version-string display and the
117+
# already-installed short-circuit. A transient api.github.com blip or the
118+
# unauthenticated rate limit (60/hr/IP) must NOT abort the install — retry a
119+
# few times, then proceed without the version string.
120+
$specificVersion = ""
121+
for ($attempt = 1; $attempt -le 3; $attempt++) {
122+
try {
123+
$rel = Invoke-RestMethod -Uri "https://api.github.com/repos/AltimateAI/altimate-code/releases/latest" -Headers @{ "User-Agent" = "altimate-install" }
124+
$specificVersion = ($rel.tag_name -replace '^v', '')
125+
if (-not [string]::IsNullOrWhiteSpace($specificVersion)) { break }
126+
} catch {}
127+
if ($attempt -lt 3) { Start-Sleep -Seconds $attempt }
121128
}
122129
if ([string]::IsNullOrWhiteSpace($specificVersion)) {
123-
Write-Err "Failed to fetch version information"
124-
exit 1
130+
Write-Muted "Could not resolve the latest version from GitHub (API unavailable) — installing the latest release anyway."
125131
}
126132
} else {
127133
$useLatest = $false
@@ -177,7 +183,7 @@ function Install-Target {
177183
}
178184

179185
Write-Host ""
180-
Write-Host "Installing $App version: $specificVersion"
186+
Write-Host "Installing $App version: $(if ($specificVersion) { $specificVersion } else { 'latest' })"
181187

182188
$tmpDir = Join-Path ([System.IO.Path]::GetTempPath()) "altimate_install_$PID"
183189
New-Item -ItemType Directory -Force -Path $tmpDir | Out-Null
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**
2+
* Latest-version resolution must be resilient, in BOTH installers.
3+
*
4+
* The `latest` install path hits api.github.com/.../releases/latest only for the
5+
* version-string display + the already-installed short-circuit — the download
6+
* itself uses releases/latest/download/<file> (server-side latest). A transient
7+
* 504 or the 60/hr/IP unauthenticated rate limit must NOT abort the install:
8+
* retry a few times, then degrade gracefully and install latest anyway.
9+
*/
10+
import { describe, test, expect } from "bun:test"
11+
import { readFileSync } from "node:fs"
12+
import { join } from "node:path"
13+
14+
const REPO_ROOT = join(import.meta.dir, "../../../..")
15+
const BASH = readFileSync(join(REPO_ROOT, "install"), "utf-8")
16+
const PS1 = readFileSync(join(REPO_ROOT, "install.ps1"), "utf-8")
17+
18+
describe("bash installer — latest-version fetch is non-fatal", () => {
19+
test("retries the releases/latest API call", () => {
20+
expect(BASH).toContain("for attempt in 1 2 3")
21+
// --fail so a 504 errors out (and retries) instead of parsing an error body.
22+
expect(BASH).toContain("curl -fsSL https://api.github.com")
23+
})
24+
25+
test("degrades gracefully instead of exiting on API failure", () => {
26+
expect(BASH).toContain("installing the latest release anyway")
27+
// The old fatal hard-fail must be gone from the latest path.
28+
expect(BASH).not.toContain("Failed to fetch version information")
29+
})
30+
31+
test("only short-circuits as already-installed on a real version match", () => {
32+
expect(BASH).toContain('[ -n "$specific_version" ] && [[ "$installed_version" == "$specific_version" ]]')
33+
})
34+
})
35+
36+
describe("PowerShell installer — latest-version fetch is non-fatal", () => {
37+
test("retries the releases/latest API call", () => {
38+
expect(PS1).toContain("for ($attempt = 1; $attempt -le 3; $attempt++)")
39+
})
40+
41+
test("degrades gracefully instead of exiting on API failure", () => {
42+
expect(PS1).toContain("installing the latest release anyway")
43+
// The old fatal hard-fail must be gone.
44+
expect(PS1).not.toContain("Failed to fetch version information")
45+
})
46+
})

0 commit comments

Comments
 (0)