Skip to content

Instantly share code, notes, and snippets.

@MohamedElashri
Last active April 22, 2025 06:16
Show Gist options
  • Save MohamedElashri/b8dabacb67d56f158dd043d5718d83fd to your computer and use it in GitHub Desktop.
Save MohamedElashri/b8dabacb67d56f158dd043d5718d83fd to your computer and use it in GitHub Desktop.
Update script for Cursor AI IDE on linux

Cursor IDE Update Script

This Python script automates the process of downloading, installing, and updating the Cursor IDE on Linux. It checks if a new version is available, updates the AppImage, and refreshes the desktop entry and icon.

Prerequisites

  • Python 3.x
  • urllib and hashlib modules (standard with Python 3)

Usage

  1. download the script:
wget https://gist.githubusercontent.com/MohamedElashri/b8dabacb67d56f158dd043d5718d83fd/raw/77f134ecf31fda96d4874458cc9c99ff5d15e1e1/update_cursor_linux.py
  1. Run the script with sudo to allow installation into /opt and update the system-wide .desktop entry:
    sudo python3 update_cursor_linux.py

The script will check for the latest version of Cursor IDE and update it if necessary.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
import json
import hashlib
import urllib.request
import urllib.error
# --- Configuration ---
API_URL = "https://www.cursor.com/api/download?platform=linux-x64&releaseTrack=stable"
ICON_URL = "https://avatars.githubusercontent.com/u/126759922?v=4"
TARGET_DIR = "/opt/cursor"
APPIMAGE_NAME = "cursor.AppImage"
DESKTOP_FILE = "/usr/share/applications/cursor.desktop"
ICON_NAME = "cursor.png"
# ----------------------
def calculate_file_hash(path, algo="sha256"):
h = hashlib.new(algo)
with open(path, "rb") as f:
while chunk := f.read(8192):
h.update(chunk)
return h.hexdigest()
def download_file(url, dest, headers=None):
req = urllib.request.Request(url, headers=headers or {})
with urllib.request.urlopen(req) as resp:
total = int(resp.info().get("Content-Length", -1))
downloaded = 0
with open(dest, "wb") as out:
while chunk := resp.read(8192):
downloaded += len(chunk)
out.write(chunk)
if total > 0:
print(f"\rDownloading… {downloaded*100/total:5.1f}%", end="", flush=True)
print("\nDone")
def fetch_download_url():
try:
with urllib.request.urlopen(API_URL) as r:
data = json.load(r)
url = data.get("downloadUrl")
if not url:
print("API response missing downloadUrl:", data, file=sys.stderr)
sys.exit(1)
return url
except (urllib.error.URLError, json.JSONDecodeError) as e:
print("Failed to fetch download info:", e, file=sys.stderr)
sys.exit(1)
os.makedirs(TARGET_DIR, exist_ok=True)
current_path = os.path.join(TARGET_DIR, APPIMAGE_NAME)
if os.path.isfile(current_path):
print("Checking for updates…")
current_hash = calculate_file_hash(current_path)
else:
print("First-time install…")
current_hash = None
print("Retrieving latest Cursor download URL…")
download_url = fetch_download_url()
print("Download URL:", download_url)
new_path = os.path.join(TARGET_DIR, "cursor_new.AppImage")
download_file(download_url, new_path, headers={"User-Agent":"Mozilla/5.0"})
new_hash = calculate_file_hash(new_path)
if new_hash == current_hash:
print("Already up to date.")
os.remove(new_path)
sys.exit(0)
print("Installing new version…")
os.chmod(new_path, 0o755)
if os.path.isfile(current_path):
os.remove(current_path)
os.rename(new_path, current_path)
icon_path = os.path.join(TARGET_DIR, ICON_NAME)
try:
download_file(ICON_URL, icon_path)
print("Icon saved to", icon_path)
except Exception:
print("Icon download failed, continuing…")
desktop_contents = f"""[Desktop Entry]
Name=Cursor AI IDE
Exec={current_path} --no-sandbox
Icon={icon_path}
Terminal=false
Type=Application
Categories=Development;Utility;
Comment=AI‑powered code editor
"""
with open(DESKTOP_FILE, "w") as f:
f.write(desktop_contents)
os.chmod(DESKTOP_FILE, 0o644)
print("Cursor installation/update complete.")
#!/usr/bin/env bash
set -euo pipefail
# Configuration
API_URL="https://www.cursor.com/api/download?platform=linux-x64&releaseTrack=stable"
ICON_URL="https://avatars.githubusercontent.com/u/126759922?v=4"
TARGET_DIR="/opt/cursor"
APPIMAGE_NAME="cursor.AppImage"
NEW_APPIMAGE_NAME="cursor_new.AppImage"
ICON_NAME="cursor.jpg"
ICON_PATH="$TARGET_DIR/$ICON_NAME"
DESKTOP_FILE="/usr/share/applications/cursor.desktop"
DESKTOP_SYMLINK="$TARGET_DIR/cursor.desktop"
# Ensure target directory exists
sudo mkdir -p "$TARGET_DIR"
# Paths
CURRENT_PATH="$TARGET_DIR/$APPIMAGE_NAME"
NEW_PATH="$TARGET_DIR/$NEW_APPIMAGE_NAME"
# Calculate hash if file exists
if [[ -f "$CURRENT_PATH" ]]; then
echo "Checking for Cursor updates..."
CURRENT_HASH=$(sha256sum "$CURRENT_PATH" | awk '{print $1}')
else
echo "Installing Cursor for the first time..."
CURRENT_HASH=""
fi
# Fetch download URL from API
echo "Fetching latest download URL..."
DOWNLOAD_URL=$(curl -fsSL "$API_URL" | jq -r '.downloadUrl')
if [[ -z "$DOWNLOAD_URL" || "$DOWNLOAD_URL" == "null" ]]; then
echo "Failed to get download URL from API response." >&2
exit 1
fi
echo "Download URL: $DOWNLOAD_URL"
# Download new AppImage
echo "Downloading Cursor AppImage..."
curl -L --progress-bar "$DOWNLOAD_URL" -o "$NEW_PATH"
# Calculate new hash
NEW_HASH=$(sha256sum "$NEW_PATH" | awk '{print $1}')
# Compare hashes
if [[ "$CURRENT_HASH" == "$NEW_HASH" ]]; then
echo "You are already using the latest version. No update necessary."
rm -f "$NEW_PATH"
exit 0
fi
# Install new version
echo "Updating Cursor..."
sudo chmod +x "$NEW_PATH"
if [[ -f "$CURRENT_PATH" ]]; then
sudo rm -f "$CURRENT_PATH"
fi
sudo mv "$NEW_PATH" "$CURRENT_PATH"
# Download and install icon
echo "Downloading icon..."
if curl -fsSL "$ICON_URL" -o "/tmp/$ICON_NAME"; then
sudo mv "/tmp/$ICON_NAME" "$ICON_PATH"
echo "Icon saved to $ICON_PATH"
else
echo "Failed to download icon; continuing without updating icon."
fi
# Create or update .desktop file
echo "Creating .desktop entry..."
sudo tee "$DESKTOP_FILE" > /dev/null <<EOF
[Desktop Entry]
Name=Cursor AI IDE
Exec=$CURRENT_PATH --no-sandbox
Icon=$ICON_PATH
Terminal=false
Type=Application
Categories=Development;Utility;
Comment=AI‑powered code editor
EOF
sudo chmod 644 "$DESKTOP_FILE"
# Create symlink to .desktop in target dir
if [[ -L "$DESKTOP_SYMLINK" || -e "$DESKTOP_SYMLINK" ]]; then
sudo rm -f "$DESKTOP_SYMLINK"
fi
sudo ln -s "$DESKTOP_FILE" "$DESKTOP_SYMLINK"
echo "Cursor installation/update complete."
@Karoid
Copy link

Karoid commented Apr 18, 2025

https://downloader.cursor.sh/linux/appImage/x64 is not working anymore
Use this script instead

#!/bin/bash

installCursor() {
    local DOWNLOAD_API_URL="https://www.cursor.com/api/download?platform=linux-x64&releaseTrack=stable"
    local ICON_URL="https://miro.medium.com/v2/resize:fit:700/1*YLg8VpqXaTyRHJoStnMuog.png"
    local APPIMAGE_PATH="/opt/cursor.appimage"
    local ICON_PATH="/opt/cursor.png"
    local DESKTOP_ENTRY_PATH="/usr/share/applications/cursor.desktop"

    echo "Checking for existing Cursor installation..."

    # Detect the user's shell
    local SHELL_NAME=$(basename "$SHELL")
    local RC_FILE=""

    case "$SHELL_NAME" in
        bash)
            RC_FILE="$HOME/.bashrc"
            ;;
        zsh)
            RC_FILE="$HOME/.zshrc"
            ;;
        fish)
            RC_FILE="$HOME/.config/fish/config.fish"
            ;;
        *)
            echo "Unsupported shell: $SHELL_NAME"
            echo "Please manually add the alias to your shell configuration file."
            return 1
            ;;
    esac

    # Notify if updating an existing installation
    if [ -f "$APPIMAGE_PATH" ]; then
        echo "Cursor AI IDE is already installed. Updating existing installation..."
    else
        echo "Performing a fresh installation of Cursor AI IDE..."
    fi

    # Install required packages if not installed
    if ! command -v curl &> /dev/null; then
        echo "curl is not installed. Installing..."
        sudo apt-get update
        sudo apt-get install -y curl || { echo "Failed to install curl."; exit 1; }
    fi
    
    if ! command -v jq &> /dev/null; then
        echo "jq is not installed. Installing..."
        sudo apt-get update
        sudo apt-get install -y jq || { echo "Failed to install jq."; exit 1; }
    fi

    # Get download URL from API
    echo "Fetching download information from Cursor API..."
    local DOWNLOAD_INFO=$(curl -s "$DOWNLOAD_API_URL")
    if [ $? -ne 0 ]; then
        echo "Failed to fetch download information from Cursor API."
        exit 1
    fi
    
    # Extract download URL from JSON response using jq
    local CURSOR_URL=$(echo "$DOWNLOAD_INFO" | jq -r '.downloadUrl')
    if [ -z "$CURSOR_URL" ] || [ "$CURSOR_URL" = "null" ]; then
        echo "Failed to extract download URL from API response."
        echo "API Response: $DOWNLOAD_INFO"
        exit 1
    fi
    
    echo "Download URL: $CURSOR_URL"

    # Download AppImage and Icon
    echo "Downloading Cursor AppImage..."
    curl -L "$CURSOR_URL" -o /tmp/cursor.appimage || { echo "Failed to download AppImage."; exit 1; }

    echo "Downloading Cursor icon..."
    curl -L "$ICON_URL" -o /tmp/cursor.png || { echo "Failed to download icon."; exit 1; }

    # Move to final destination
    echo "Installing Cursor files..."
    sudo mv /tmp/cursor.appimage "$APPIMAGE_PATH"
    sudo chmod +x "$APPIMAGE_PATH"
    sudo mv /tmp/cursor.png "$ICON_PATH"

    # Create a .desktop entry
    echo "Creating .desktop entry..."
    sudo bash -c "cat > $DESKTOP_ENTRY_PATH" <<EOL
[Desktop Entry]
Name=Cursor AI IDE
Exec=$APPIMAGE_PATH --no-sandbox
Icon=$ICON_PATH
Type=Application
Categories=Development;
EOL

    # Add alias to the appropriate RC file
    echo "Adding cursor alias to $RC_FILE..."
    if [ "$SHELL_NAME" = "fish" ]; then
        # Fish shell uses a different syntax for functions
        if ! grep -q "function cursor" "$RC_FILE"; then
            echo "function cursor" >> "$RC_FILE"
            echo "    /opt/cursor.appimage --no-sandbox \$argv > /dev/null 2>&1 & disown" >> "$RC_FILE"
            echo "end" >> "$RC_FILE"
        else
            echo "Alias already exists in $RC_FILE."
        fi
    else
        if ! grep -q "function cursor" "$RC_FILE"; then
            cat >> "$RC_FILE" <<EOL
# Cursor alias
function cursor() {
    /opt/cursor.appimage --no-sandbox "\${@}" > /dev/null 2>&1 & disown
}
EOL
        else
            echo "Alias already exists in $RC_FILE."
        fi
    fi

    # Inform the user to reload the shell
    echo "To apply changes, please restart your terminal or run the following command:"
    echo "    source $RC_FILE"

    echo "Cursor AI IDE installation or update complete. You can find it in your application menu."
}

installCursor

@MohamedElashri
Copy link
Author

downloader.cursor.sh/linux/appImage/x64 is not working anymore Use this script instead

#!/bin/bash

installCursor() {
    local DOWNLOAD_API_URL="https://www.cursor.com/api/download?platform=linux-x64&releaseTrack=stable"
    local ICON_URL="https://miro.medium.com/v2/resize:fit:700/1*YLg8VpqXaTyRHJoStnMuog.png"
    local APPIMAGE_PATH="/opt/cursor.appimage"
    local ICON_PATH="/opt/cursor.png"
    local DESKTOP_ENTRY_PATH="/usr/share/applications/cursor.desktop"

    echo "Checking for existing Cursor installation..."

    # Detect the user's shell
    local SHELL_NAME=$(basename "$SHELL")
    local RC_FILE=""

    case "$SHELL_NAME" in
        bash)
            RC_FILE="$HOME/.bashrc"
            ;;
        zsh)
            RC_FILE="$HOME/.zshrc"
            ;;
        fish)
            RC_FILE="$HOME/.config/fish/config.fish"
            ;;
        *)
            echo "Unsupported shell: $SHELL_NAME"
            echo "Please manually add the alias to your shell configuration file."
            return 1
            ;;
    esac

    # Notify if updating an existing installation
    if [ -f "$APPIMAGE_PATH" ]; then
        echo "Cursor AI IDE is already installed. Updating existing installation..."
    else
        echo "Performing a fresh installation of Cursor AI IDE..."
    fi

    # Install required packages if not installed
    if ! command -v curl &> /dev/null; then
        echo "curl is not installed. Installing..."
        sudo apt-get update
        sudo apt-get install -y curl || { echo "Failed to install curl."; exit 1; }
    fi
    
    if ! command -v jq &> /dev/null; then
        echo "jq is not installed. Installing..."
        sudo apt-get update
        sudo apt-get install -y jq || { echo "Failed to install jq."; exit 1; }
    fi

    # Get download URL from API
    echo "Fetching download information from Cursor API..."
    local DOWNLOAD_INFO=$(curl -s "$DOWNLOAD_API_URL")
    if [ $? -ne 0 ]; then
        echo "Failed to fetch download information from Cursor API."
        exit 1
    fi
    
    # Extract download URL from JSON response using jq
    local CURSOR_URL=$(echo "$DOWNLOAD_INFO" | jq -r '.downloadUrl')
    if [ -z "$CURSOR_URL" ] || [ "$CURSOR_URL" = "null" ]; then
        echo "Failed to extract download URL from API response."
        echo "API Response: $DOWNLOAD_INFO"
        exit 1
    fi
    
    echo "Download URL: $CURSOR_URL"

    # Download AppImage and Icon
    echo "Downloading Cursor AppImage..."
    curl -L "$CURSOR_URL" -o /tmp/cursor.appimage || { echo "Failed to download AppImage."; exit 1; }

    echo "Downloading Cursor icon..."
    curl -L "$ICON_URL" -o /tmp/cursor.png || { echo "Failed to download icon."; exit 1; }

    # Move to final destination
    echo "Installing Cursor files..."
    sudo mv /tmp/cursor.appimage "$APPIMAGE_PATH"
    sudo chmod +x "$APPIMAGE_PATH"
    sudo mv /tmp/cursor.png "$ICON_PATH"

    # Create a .desktop entry
    echo "Creating .desktop entry..."
    sudo bash -c "cat > $DESKTOP_ENTRY_PATH" <<EOL
[Desktop Entry]
Name=Cursor AI IDE
Exec=$APPIMAGE_PATH --no-sandbox
Icon=$ICON_PATH
Type=Application
Categories=Development;
EOL

    # Add alias to the appropriate RC file
    echo "Adding cursor alias to $RC_FILE..."
    if [ "$SHELL_NAME" = "fish" ]; then
        # Fish shell uses a different syntax for functions
        if ! grep -q "function cursor" "$RC_FILE"; then
            echo "function cursor" >> "$RC_FILE"
            echo "    /opt/cursor.appimage --no-sandbox \$argv > /dev/null 2>&1 & disown" >> "$RC_FILE"
            echo "end" >> "$RC_FILE"
        else
            echo "Alias already exists in $RC_FILE."
        fi
    else
        if ! grep -q "function cursor" "$RC_FILE"; then
            cat >> "$RC_FILE" <<EOL
# Cursor alias
function cursor() {
    /opt/cursor.appimage --no-sandbox "\${@}" > /dev/null 2>&1 & disown
}
EOL
        else
            echo "Alias already exists in $RC_FILE."
        fi
    fi

    # Inform the user to reload the shell
    echo "To apply changes, please restart your terminal or run the following command:"
    echo "    source $RC_FILE"

    echo "Cursor AI IDE installation or update complete. You can find it in your application menu."
}

installCursor

Thanks, I updated the script

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment