Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 38 additions & 5 deletions install.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -191,15 +191,48 @@ function Ensure-Node {
}
}

# Reads the latest stable tag off the release page redirect. The GitHub API caps
# unauthenticated callers at 60 requests/hour per IP, which a shared egress can
# exhaust; the release page carries no API quota. Returns "" when the redirect is
# missing or does not name a stable tag, so the caller can fail with its own message.
function Resolve-RavenLatestVersion {
$target = ""
try {
$response = Invoke-WebRequest "https://github.com/EverMind-AI/Raven/releases/latest" -MaximumRedirection 0 -UseBasicParsing -ErrorAction Stop
$target = [string]$response.Headers.Location
} catch {
# Windows PowerShell raises on an unfollowed redirect; the Location header
# still rides on the exception's response.
$failed = $_.Exception.Response
if ($failed) {
try { $target = [string]$failed.Headers.Location } catch { $target = "" }
if (-not $target) {
try { $target = [string]$failed.Headers.GetValues("Location")[0] } catch { $target = "" }
}
}
}
if ($target -match "^https://github\.com/EverMind-AI/Raven/releases/tag/v([0-9]+\.[0-9]+\.[0-9]+)$") {
return $Matches[1]
}
return ""
}

function Resolve-RavenWheel {
if ($env:RAVEN_WHEEL_URL) { return $env:RAVEN_WHEEL_URL }
Write-Info "Resolving the latest Raven release from GitHub..."
$release = Invoke-RestMethod "https://api.github.com/repos/EverMind-AI/Raven/releases/latest" -Headers @{ "User-Agent" = "raven-installer" }
$asset = $release.assets | Where-Object { $_.browser_download_url -match "/raven-[^/]+\.whl$" } | Select-Object -First 1
if (-not $asset) {
Fail "Could not resolve the latest Raven release wheel from GitHub. Set RAVEN_WHEEL_URL to a wheel URL."
try {
$release = Invoke-RestMethod "https://api.github.com/repos/EverMind-AI/Raven/releases/latest" -Headers @{ "User-Agent" = "raven-installer" }
$asset = $release.assets | Where-Object { $_.browser_download_url -match "/raven-[^/]+\.whl$" } | Select-Object -First 1
if ($asset) { return $asset.browser_download_url }
Write-Warn "GitHub API returned no release wheel; falling back to the release page."
} catch {
Write-Warn "GitHub API lookup failed ($($_.Exception.Message)); falling back to the release page."
}
$version = Resolve-RavenLatestVersion
if (-not $version) {
Fail "Could not resolve the latest Raven release wheel from GitHub. Retry later, or set RAVEN_WHEEL_URL to a wheel URL."
}
return $asset.browser_download_url
return "https://github.com/EverMind-AI/Raven/releases/download/v$version/raven-$version-py3-none-any.whl"
}

function Resolve-RavenConstraints([string]$WheelUrl) {
Expand Down
36 changes: 34 additions & 2 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,42 @@ install_raven() {
wheel_url="${RAVEN_WHEEL_URL:-}"
if [ -z "$wheel_url" ]; then
info "Resolving the latest raven release from GitHub..."
wheel_url="$(curl -fsSL "https://api.github.com/repos/EverMind-AI/raven/releases/latest" 2>/dev/null \
wheel_url="$(curl -fsSL "https://api.github.com/repos/EverMind-AI/Raven/releases/latest" 2>/dev/null \
| grep -oE 'https://[^"]*/raven-[^"]*\.whl' | head -n1)"
fi
[ -n "$wheel_url" ] || die "Could not resolve the latest raven release wheel from GitHub (check network, or set RAVEN_WHEEL_URL to a wheel URL)."
if [ -z "$wheel_url" ]; then
# The GitHub API caps unauthenticated callers at 60 requests/hour per IP, so a
# shared egress can exhaust it. The release page carries no API quota: its
# redirect names the latest stable tag, and the wheel URL is derived from it.
warn "GitHub API returned no release wheel; falling back to the release page."
tag="$(curl -fsS -o /dev/null -w '%{redirect_url}' \
"https://github.com/EverMind-AI/Raven/releases/latest")" || tag=""
# Same shape the CLI and install.ps1 enforce: the redirect must land on this
# repository's tag page, and the version must be exactly three numeric fields
# with no leading zeros.
case "$tag" in
https://github.com/EverMind-AI/Raven/releases/tag/v*) version="${tag##*/}" ;;
*) version="" ;;
esac
version="${version#v}"
case "$version" in
*.*.*.*) version="" ;;
*.*.*) ;;
*) version="" ;;
esac
if [ -n "$version" ]; then
v_rest="${version#*.}"
for field in "${version%%.*}" "${v_rest%%.*}" "${v_rest#*.}"; do
case "$field" in
""|*[!0-9]*|0[0-9]*) version="" ;;
esac
done
fi
if [ -n "$version" ]; then
wheel_url="https://github.com/EverMind-AI/Raven/releases/download/v${version}/raven-${version}-py3-none-any.whl"
fi
fi
[ -n "$wheel_url" ] || die "Could not resolve the latest raven release wheel from GitHub. Retry later, or set RAVEN_WHEEL_URL to a wheel URL."
# Derive the locked-constraints URL from the wheel URL (same release dir) so
# the constraints always match the wheel being installed, including when
# RAVEN_WHEEL_URL pins an older wheel. Missing asset / download failure ->
Expand Down
21 changes: 11 additions & 10 deletions raven/cli/update_notice.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
``update_command`` (see ``ui-tui/src/components/appChrome.tsx``); this module
is what fills those in.

The live check hits the GitHub releases API, which is too slow to run on the
session-create hot path, so we keep a small cache in the runtime cache dir and
refresh it in a daemon thread at most once a day. A launch therefore shows the
notice based on the *cached* latest version; the first launch after a release
lands refreshes the cache and the notice appears on the next launch. Any
network or parse failure is swallowed -- an update nudge must never break
startup.
The live check reads the release page redirect (not the releases API, whose
unauthenticated quota is 60 requests per hour per IP -- a daily check from every
install behind one egress is enough to drain it, and a nudge must not cost the
budget that ``raven upgrade`` needs). It is still too slow for the session-create
hot path, so we keep a small cache in the runtime cache dir and refresh it in a
daemon thread at most once a day. A launch therefore shows the notice based on the
*cached* latest version; the first launch after a release lands refreshes the cache
and the notice appears on the next launch. Any network or parse failure is
swallowed -- an update nudge must never break startup.

Set ``RAVEN_NO_UPDATE_CHECK=1`` to opt out of both the fetch and the hint.
"""
Expand Down Expand Up @@ -111,10 +113,9 @@ def _refresh() -> None:
keep = previous if isinstance(previous, str) else None

try:
from raven.cli.upgrade_commands import _fetch_latest_release
from raven.cli.upgrade_commands import fetch_latest_version

release = _fetch_latest_release()
_write_cache(release.version, now=time.time())
_write_cache(fetch_latest_version(), now=time.time())
except Exception:
# Offline, rate-limited, or the latest release is a draft/prerelease.
# Stamp checked_at anyway so we back off for a full TTL instead of
Expand Down
Loading
Loading