diff --git a/cursor-release.nix b/cursor-release.nix index 8bc9127..c92fa08 100644 --- a/cursor-release.nix +++ b/cursor-release.nix @@ -3,7 +3,7 @@ # # Notes: # - `sha256` values are Nix base32 hashes (as used by `fetchurl { sha256 = "..."; }`). - version = "2.2.44"; + version = "2.2.43"; sha256 = { x86_64-linux = "1bgifjyscqymbz0srw8np9pc88gwcccic3z4cnydyfskxhnyj0sr"; diff --git a/flake.nix b/flake.nix index 7b403d6..74cbba8 100644 --- a/flake.nix +++ b/flake.nix @@ -116,7 +116,12 @@ pkgs = pkgsFor system; version = cursorRelease.version; channel = channelForSystem.${system} or (throw "Unsupported system: ${system}"); - url = "https://api2.cursor.sh/updates/download/golden/${channel}/cursor/${version}"; + # Prefer the stable resolved downloads.cursor.com URL stored by ./update-cursor.sh. + # Fallback to the API URL for older cursor-release.nix files. + url = + if cursorRelease ? url && cursorRelease.url ? ${system} + then cursorRelease.url.${system} + else "https://api2.cursor.sh/updates/download/golden/${channel}/cursor/${version}"; sha256 = cursorRelease.sha256.${system} or "0000000000000000000000000000000000000000000000000000"; in { diff --git a/update-cursor.sh b/update-cursor.sh index cb9bb9f..4a47bd1 100755 --- a/update-cursor.sh +++ b/update-cursor.sh @@ -15,10 +15,17 @@ RELEASE_FILE="$SCRIPT_DIR/cursor-release.nix" CHANNEL_FOR_SYSTEM_x86_64_linux="linux-x64" CHANNEL_FOR_SYSTEM_aarch64_linux="linux-arm64" -# Get a redirect URL for a given "track" pointer (e.g. latest, 2.2). +# Extract Cursor semver from a resolved download URL (Location header). +extract_version_from_url() { + local url="${1:-}" + echo "$url" | grep -Eo 'Cursor-[0-9]+\.[0-9]+\.[0-9]+' | sed 's/Cursor-//' || true +} + +# Get a redirect URL for a given pointer (e.g. latest, 2.2, 2.2.44) and channel. get_redirect_url() { - local track="${1:?track is required}" - local url="https://api2.cursor.sh/updates/download/golden/linux-x64/cursor/${track}" + local channel="${1:?channel is required}" + local pointer="${2:?pointer is required}" + local url="https://api2.cursor.sh/updates/download/golden/${channel}/cursor/${pointer}" # Avoid stale CDN/proxy cache results for fast-moving pointers like "latest". curl -sS -I \ @@ -29,12 +36,58 @@ get_redirect_url() { | tr -d '\r\n' } -# Resolve a track pointer (e.g. latest, 2.2) into an actual semver (e.g. 2.2.44). +# Resolve an exact version to a stable downloads.cursor.com URL. +# +# Guardrail: Cursor's API has been observed to sometimes redirect an "exact version" URL +# (e.g. /cursor/2.2.44) to an older AppImage (e.g. 2.2.43). When that happens, we retry via +# the major.minor pointer (e.g. /cursor/2.2) and only proceed if it resolves to the requested +# version. +resolve_download_url_for_version() { + local channel="${1:?channel is required}" + local version="${2:?version is required}" + + local redirect_url resolved_version + redirect_url="$(get_redirect_url "$channel" "$version")" + if [[ -z "$redirect_url" ]]; then + echo "Error: Could not resolve Location header for ${channel} ${version}" >&2 + return 1 + fi + + resolved_version="$(extract_version_from_url "$redirect_url")" + if [[ "$resolved_version" == "$version" ]]; then + echo "$redirect_url" + return 0 + fi + + local minor_pointer fallback_redirect fallback_resolved + minor_pointer="$(get_track_from_version "$version")" + echo "Warning: ${channel} ${version} resolved to ${resolved_version:-unknown}; retrying via pointer ${minor_pointer}" >&2 + fallback_redirect="$(get_redirect_url "$channel" "$minor_pointer")" + if [[ -z "$fallback_redirect" ]]; then + echo "Error: Could not resolve Location header for fallback ${channel} ${minor_pointer}" >&2 + return 1 + fi + + fallback_resolved="$(extract_version_from_url "$fallback_redirect")" + if [[ "$fallback_resolved" != "$version" ]]; then + echo "Error: Could not resolve requested version $version for channel $channel." >&2 + echo "Tried:" >&2 + echo " - /cursor/${version} -> ${resolved_version:-unknown} ($redirect_url)" >&2 + echo " - /cursor/${minor_pointer} -> ${fallback_resolved:-unknown} ($fallback_redirect)" >&2 + echo "Upstream pointers/caches are inconsistent; try again later." >&2 + return 1 + fi + + echo "$fallback_redirect" +} + +# Resolve a pointer (e.g. latest, 2.2) into an actual semver (e.g. 2.2.44). get_latest_version() { local track="${1:-latest}" local redirect_url - redirect_url="$(get_redirect_url "$track")" + # Version discovery can just use linux-x64; the resolved version is shared across architectures. + redirect_url="$(get_redirect_url "$CHANNEL_FOR_SYSTEM_x86_64_linux" "$track")" if [[ -n "$redirect_url" ]]; then # Extract version from URL like: .../Cursor-2.2.44-x86_64.AppImage @@ -75,7 +128,7 @@ semver_max() { # Function to compute sha256 for a given system+version prefetch_sha256() { local system="$1" - local version="$2" + local url="$2" local channel="" case "$system" in @@ -84,15 +137,13 @@ prefetch_sha256() { *) echo "Error: Unsupported system: $system" >&2; return 1 ;; esac - local url="https://api2.cursor.sh/updates/download/golden/${channel}/cursor/${version}" - if ! command -v nix-prefetch-url >/dev/null 2>&1; then echo "Error: nix-prefetch-url not found. Please install Nix (or nix-prefetch-url) and retry." >&2 return 1 fi # IMPORTANT: this function is captured by command substitution. Keep stdout as the hash only. - echo "Prefetching ($system) from: $url" >&2 + echo "Prefetching ($system) from resolved URL: $url" >&2 local out hash out="$(nix-prefetch-url --type sha256 "$url" 2>&1)" @@ -114,9 +165,12 @@ update_release() { echo "Computing hashes for version: $version" - local sha_x86 sha_aarch64 - sha_x86="$(prefetch_sha256 x86_64-linux "$version")" - sha_aarch64="$(prefetch_sha256 aarch64-linux "$version")" + local url_x86 url_aarch64 sha_x86 sha_aarch64 + url_x86="$(resolve_download_url_for_version "$CHANNEL_FOR_SYSTEM_x86_64_linux" "$version")" + url_aarch64="$(resolve_download_url_for_version "$CHANNEL_FOR_SYSTEM_aarch64_linux" "$version")" + + sha_x86="$(prefetch_sha256 x86_64-linux "$url_x86")" + sha_aarch64="$(prefetch_sha256 aarch64-linux "$url_aarch64")" echo "x86_64-linux sha256: $sha_x86" echo "aarch64-linux sha256: $sha_aarch64" @@ -132,6 +186,11 @@ update_release() { # - \`sha256\` values are Nix base32 hashes (as used by \`fetchurl { sha256 = "..."; }\`). version = "$version"; + url = { + x86_64-linux = "$url_x86"; + aarch64-linux = "$url_aarch64"; + }; + sha256 = { x86_64-linux = "$sha_x86"; aarch64-linux = "$sha_aarch64";