261 lines
8.4 KiB
Bash
Executable File
261 lines
8.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
set -euo pipefail
|
|
|
|
# Script to manually update Cursor version/hashes used by the flake
|
|
# Usage:
|
|
# ./update-cursor.sh # auto: pick highest of ("latest" pointer, current major.minor pointer)
|
|
# ./update-cursor.sh 2.2.44 # pin exact version
|
|
# ./update-cursor.sh latest # follow the "latest" pointer only
|
|
# ./update-cursor.sh 2.2 # follow a major.minor pointer only
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
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).
|
|
get_redirect_url() {
|
|
local track="${1:?track is required}"
|
|
local url="https://api2.cursor.sh/updates/download/golden/linux-x64/cursor/${track}"
|
|
|
|
# Avoid stale CDN/proxy cache results for fast-moving pointers like "latest".
|
|
curl -sS -I \
|
|
-H 'Cache-Control: no-cache' \
|
|
-H 'Pragma: no-cache' \
|
|
"$url" \
|
|
| awk 'BEGIN{IGNORECASE=1} $1=="location:" {print $2; exit}' \
|
|
| tr -d '\r\n'
|
|
}
|
|
|
|
# Resolve a track 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")"
|
|
|
|
if [[ -n "$redirect_url" ]]; then
|
|
# Extract version from URL like: .../Cursor-2.2.44-x86_64.AppImage
|
|
echo "$redirect_url" | grep -Eo 'Cursor-[0-9]+\.[0-9]+\.[0-9]+' | sed 's/Cursor-//'
|
|
else
|
|
echo "Error: Could not get redirect URL for track: $track" >&2
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# True if arg looks like a full semver (e.g. 2.2.44).
|
|
is_full_semver() {
|
|
[[ "${1:-}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]
|
|
}
|
|
|
|
# True if arg looks like a pointer we can resolve (e.g. latest, 2.2).
|
|
is_pointer() {
|
|
[[ "${1:-}" == "latest" || "${1:-}" =~ ^[0-9]+\.[0-9]+$ ]]
|
|
}
|
|
|
|
# Function to get current version from cursor-release.nix
|
|
get_current_version() {
|
|
grep -o 'version = "[^"]*"' "$RELEASE_FILE" | head -1 | cut -d'"' -f2
|
|
}
|
|
|
|
# Derive major.minor track from a full version (e.g. 2.2.43 -> 2.2).
|
|
get_track_from_version() {
|
|
local version="${1:?version is required}"
|
|
echo "$version" | awk -F. '{print $1 "." $2}'
|
|
}
|
|
|
|
# Return the highest of the provided dotted versions using natural version sort.
|
|
semver_max() {
|
|
# Accepts N args; prints the max.
|
|
printf '%s\n' "$@" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -n1
|
|
}
|
|
|
|
# Function to compute sha256 for a given system+version
|
|
prefetch_sha256() {
|
|
local system="$1"
|
|
local version="$2"
|
|
local channel=""
|
|
|
|
case "$system" in
|
|
x86_64-linux) channel="$CHANNEL_FOR_SYSTEM_x86_64_linux" ;;
|
|
aarch64-linux) channel="$CHANNEL_FOR_SYSTEM_aarch64_linux" ;;
|
|
*) 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
|
|
|
|
local out hash
|
|
out="$(nix-prefetch-url --type sha256 "$url" 2>&1)"
|
|
echo "$out" >&2
|
|
|
|
# Extract the Nix base32 sha256 hash (52 chars, Nix alphabet).
|
|
hash="$(echo "$out" | grep -Eo '[0-9abcdfghijklmnpqrsvwxyz]{52}' | tail -n1 || true)"
|
|
if [[ -z "$hash" ]]; then
|
|
echo "Error: Could not parse sha256 from nix-prefetch-url output for $system" >&2
|
|
return 1
|
|
fi
|
|
|
|
echo "$hash"
|
|
}
|
|
|
|
# Function to update cursor-release.nix
|
|
update_release() {
|
|
local version="$1"
|
|
|
|
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")"
|
|
|
|
echo "x86_64-linux sha256: $sha_x86"
|
|
echo "aarch64-linux sha256: $sha_aarch64"
|
|
|
|
# Create backup
|
|
cp "$RELEASE_FILE" "$RELEASE_FILE.backup"
|
|
|
|
cat > "$RELEASE_FILE" <<EOF
|
|
{
|
|
# Managed by ./update-cursor.sh
|
|
#
|
|
# Notes:
|
|
# - \`sha256\` values are Nix base32 hashes (as used by \`fetchurl { sha256 = "..."; }\`).
|
|
version = "$version";
|
|
|
|
sha256 = {
|
|
x86_64-linux = "$sha_x86";
|
|
aarch64-linux = "$sha_aarch64";
|
|
};
|
|
}
|
|
EOF
|
|
|
|
echo "Updated cursor-release.nix with version $version"
|
|
}
|
|
|
|
# Function to test the flake
|
|
test_flake() {
|
|
echo "Testing flake..."
|
|
if command -v nix >/dev/null 2>&1; then
|
|
nix flake check --no-build
|
|
echo "Flake check passed!"
|
|
else
|
|
echo "Warning: nix command not found. Skipping flake check."
|
|
fi
|
|
}
|
|
|
|
# Main logic
|
|
main() {
|
|
local target_version="${1:-}"
|
|
|
|
echo "Cursor Flake Updater"
|
|
echo "==================="
|
|
|
|
# Get current version
|
|
local current_version
|
|
current_version=$(get_current_version)
|
|
echo "Current version: $current_version"
|
|
|
|
# Determine target version
|
|
if [[ -n "$target_version" ]]; then
|
|
if is_full_semver "$target_version"; then
|
|
echo "Target version (pinned): $target_version"
|
|
elif is_pointer "$target_version"; then
|
|
echo "Target pointer: $target_version"
|
|
target_version="$(get_latest_version "$target_version")"
|
|
echo "Resolved version: $target_version"
|
|
else
|
|
echo "Error: Invalid argument: $target_version" >&2
|
|
echo "Expected: full version (e.g. 2.2.44) or pointer (latest or 2.2)" >&2
|
|
exit 1
|
|
fi
|
|
else
|
|
# Auto mode:
|
|
# - Check "latest" to allow moving to new major/minor (e.g. 2.3.x)
|
|
# - Check current major.minor line to avoid missing patches when "latest" lags (e.g. 2.2.44 vs latest=2.2.43)
|
|
# - Also check latest's own major.minor line (if latest=2.3.0 but 2.3 pointer is 2.3.1)
|
|
local current_track v_latest latest_track v_latest_track v_current_track
|
|
current_track="$(get_track_from_version "$current_version")"
|
|
|
|
echo "Fetching pointers: latest, $current_track"
|
|
v_latest="$(get_latest_version latest)"
|
|
latest_track="$(get_track_from_version "$v_latest")"
|
|
v_current_track="$(get_latest_version "$current_track")"
|
|
|
|
if [[ "$latest_track" != "$current_track" ]]; then
|
|
echo "Latest is on a different minor: $latest_track (also checking it)"
|
|
v_latest_track="$(get_latest_version "$latest_track")"
|
|
else
|
|
v_latest_track="$v_latest"
|
|
fi
|
|
|
|
target_version="$(semver_max "$v_latest" "$v_current_track" "$v_latest_track")"
|
|
echo "Resolved: latest=$v_latest pointer($current_track)=$v_current_track pointer($latest_track)=$v_latest_track -> chosen=$target_version"
|
|
fi
|
|
|
|
# Check if update is needed
|
|
if [[ "$target_version" == "$current_version" ]]; then
|
|
echo "No update needed. Current version is up to date."
|
|
if [[ -n "${GITHUB_OUTPUT:-}" ]]; then
|
|
echo "CURSOR_VERSION_INFO=no_update" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
exit 0
|
|
fi
|
|
|
|
echo "Update needed: $current_version -> $target_version"
|
|
|
|
# Check if running in CI/GitHub Actions (auto-confirm)
|
|
if [[ -n "${CI:-}" ]] || [[ -n "${GITHUB_ACTIONS:-}" ]]; then
|
|
echo "Running in CI mode, auto-confirming update..."
|
|
REPLY="y"
|
|
else
|
|
read -p "Do you want to proceed with the update? (y/N): " -n 1 -r
|
|
echo
|
|
fi
|
|
|
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
|
update_release "$target_version"
|
|
test_flake
|
|
echo "Update completed successfully!"
|
|
if [[ -n "${GITHUB_OUTPUT:-}" ]]; then
|
|
echo "CURSOR_VERSION_INFO=completed:$current_version:$target_version" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
echo "You can now commit the changes:"
|
|
echo " git add flake.nix cursor-release.nix"
|
|
echo " git commit -m \"Update Cursor to version $target_version\""
|
|
else
|
|
echo "Update cancelled."
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Check dependencies
|
|
check_dependencies() {
|
|
local missing_deps=()
|
|
|
|
if ! command -v curl >/dev/null 2>&1; then
|
|
missing_deps+=("curl")
|
|
fi
|
|
if [[ ! -f "$RELEASE_FILE" ]]; then
|
|
missing_deps+=("cursor-release.nix (missing file)")
|
|
fi
|
|
|
|
if [[ ${#missing_deps[@]} -gt 0 ]]; then
|
|
echo "Error: Missing required dependencies: ${missing_deps[*]}"
|
|
echo "Please install them and try again."
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Run main function
|
|
check_dependencies
|
|
main "$@" |