Skip to content

Instantly share code, notes, and snippets.

@benjaminalt
Created October 15, 2025 09:11
Show Gist options
  • Select an option

  • Save benjaminalt/f91391e0befd6a8b09e690b195417fbe to your computer and use it in GitHub Desktop.

Select an option

Save benjaminalt/f91391e0befd6a8b09e690b195417fbe to your computer and use it in GitHub Desktop.
Script to install and update Cursor on Ubuntu. Tested on 24.04.
#!/bin/bash
# Define colors and styling
RED='\e[1;31m'
GREEN='\e[1;32m'
BLUE='\e[1;34m'
YELLOW='\e[1;33m'
CYAN='\e[1;36m'
PURPLE='\e[1;35m'
NC='\e[0m' # No Colora
BOLD='\e[1m'
UNDERLINE='\e[4m'
# Define variables
APP_DIR="$HOME/Tools/cursor"
APP_IMAGE="$APP_DIR/cursor.AppImage"
EXTRACT_DIR="$APP_DIR/squashfs-root"
ICON_DIR="$APP_DIR/icons"
ICON_PATH="$ICON_DIR/cursor_icon.png"
ICON_URL="https://camo.githubusercontent.com/a0307a3865517874a8d9f9904ef532de5f0600a16cbbb4c6f0204904e0e4e172/68747470733a2f2f6d696e746c6966792e73332d75732d776573742d312e616d617a6f6e6177732e636f6d2f637572736f722f696d616765732f6c6f676f2f6170702d6c6f676f2e737667"
API_URL="https://api2.cursor.sh/updates/api/update/linux-x64/cursor/0.48.9/d024087b1e0e0d0110800b253e063c65497b77a95213eb7e063df2bf7e8f6a07/stable"
# Function to display messages
function info {
echo -e "${BLUE}[INFO]${NC} $1"
}
function success {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
function error {
echo -e "${RED}[ERROR]${NC} $1"
}
function warning {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
# Check if curl is installed
if ! command -v curl &> /dev/null; then
error "curl is not installed on your system."
echo -e "${YELLOW}┌─ Prerequisites Check ────────────────────────┐${NC}"
echo -e "${YELLOW}│${NC} curl is required to download Cursor updates."
echo -e "${YELLOW}└─────────────────────────────────────────┘${NC}"
read -p "$(echo -e "${CYAN}Would you like to install curl now? (y/n):${NC} ")" install_curl
if [[ "$install_curl" != "y" && "$install_curl" != "Y" ]]; then
echo ""
warning "Please install curl and run this script again."
echo -e "${PURPLE}Tip: You can install curl using:${NC}"
echo -e "${CYAN} sudo apt install curl${NC} (for Debian/Ubuntu)"
echo -e "${CYAN} sudo dnf install curl${NC} (for Fedora)"
echo -e "${CYAN} sudo pacman -S curl${NC} (for Arch Linux)"
echo ""
exit 1
else
echo ""
info "Installing curl..."
if sudo apt-get update && sudo apt-get install -y curl; then
success "curl installed successfully!"
echo ""
else
error "Failed to install curl. Please install it manually and run this script again."
exit 1
fi
fi
fi
# Spinner animation for processes
function show_spinner {
local pid=$1
local message=$2
local spin='-\|/'
local i=0
while kill -0 $pid 2>/dev/null; do
i=$(( (i+1) % 4 ))
printf "\r${YELLOW}[%c]${NC} ${message}" "${spin:$i:1}"
sleep .1
done
printf "\r \r"
}
# Display banner
clear
echo -e "${CYAN}╔═══════════════════════════════════════════════════╗${NC}"
echo -e "${CYAN}║ ║${NC}"
echo -e "${CYAN}║${BOLD} 🚀 CURSOR EDITOR - AUTOMATIC UPDATER ${NC}${CYAN} ║${NC}"
echo -e "${CYAN}║ ║${NC}"
echo -e "${CYAN}╚═══════════════════════════════════════════════════╝${NC}"
echo ""
# Start update process
info "Starting Cursor update process..."
# Create application directory if it doesn't exist
if [ ! -d "$APP_DIR" ]; then
mkdir -p "$APP_DIR"
success "Created application directory: $APP_DIR"
fi
# Navigate to application directory
cd "$APP_DIR" || { error "Failed to navigate to $APP_DIR"; exit 1; }
# Create icons directory if it doesn't exist
if [ ! -d "$ICON_DIR" ]; then
mkdir -p "$ICON_DIR"
fi
# Download icon
info "${UNDERLINE}Downloading Cursor icon...${NC}"
if wget -q "$ICON_URL" -O "$ICON_PATH" 2>&1; then
success "Icon downloaded successfully."
else
warning "Failed to download icon from URL. Will use default icon instead."
ICON_PATH=""
fi
# Fetch the latest version information from the API
echo ""
info "${UNDERLINE}Fetching the latest version information...${NC}"
echo ""
response=$(curl -s "$API_URL")
# Extract the download URL from the response
download_url=$(echo "$response" | grep -oP '(?<="url":")[^"]+')
appimage_url="${download_url%.zsync}"
version=$(echo "$appimage_url" | grep -oP 'Cursor-\K[0-9.]+')
if [[ -z "$appimage_url" ]]; then
error "Failed to extract the AppImage URL from the API response."
read -p "$(echo -e "${CYAN}Press Enter to exit...${NC}")"
exit 1
fi
echo -e "${PURPLE}┌─ Version Info ───────────────────────────┐${NC}"
echo -e "${PURPLE}│${NC} Latest version detected: ${BOLD}$version${NC}"
echo -e "${PURPLE}└─────────────────────────────────────────┘${NC}"
echo ""
info "${UNDERLINE}Downloading Cursor version $version...${NC}"
echo ""
echo -e "${CYAN}┌─ Download Progress ──────────────────────┐${NC}"
if wget --progress=bar:force --show-progress "$appimage_url" -O "$APP_IMAGE" 2>&1; then
echo -e "${CYAN}└─────────────────────────────────────────┘${NC}"
echo ""
success "Download completed successfully."
else
echo -e "${CYAN}└─────────────────────────────────────────┘${NC}"
echo ""
error "Failed to download the latest AppImage."
read -p "$(echo -e "${CYAN}Press Enter to exit...${NC}")"
exit 1
fi
echo ""
info "${UNDERLINE}Setting up permissions...${NC}"
# Make the AppImage executable
chmod +x "$APP_IMAGE"
chown $USER:$USER "$APP_IMAGE"
success "Permissions set."
# Fix ownership and remove previous extracted files
if [ -d "$EXTRACT_DIR" ]; then
warning "Previous installation detected."
echo -e "${YELLOW}┌─ Cleanup Process ─────────────────────────┐${NC}"
echo -e "${YELLOW}│${NC} Fixing permissions and removing old files..."
echo -e "${YELLOW}└─────────────────────────────────────────┘${NC}"
sudo chown -R $USER:$USER "$EXTRACT_DIR"
rm -rf "$EXTRACT_DIR"
success "Cleanup completed."
fi
echo ""
info "${UNDERLINE}Extracting the AppImage...${NC}"
echo -e "${YELLOW}┌─ Extraction Process ───────────────────────┐${NC}"
echo -e "${YELLOW}│${NC} This may take a moment. Please wait..."
echo -e "${YELLOW}└─────────────────────────────────────────┘${NC}"
# Add a simple animation during extraction
echo -n " "
for i in {1..30}; do
echo -n "${CYAN}▓${NC}"
"$APP_IMAGE" --appimage-extract > /dev/null 2>&1 &
extract_pid=$!
sleep 0.1
if [ ! -d "$EXTRACT_DIR" ]; then
continue
else
# Fill the rest of the progress bar
remaining=$((30 - i))
for j in $(seq 1 $remaining); do
echo -n "${CYAN}▓${NC}"
done
break
fi
done
echo ""
if [ -d "$EXTRACT_DIR" ]; then
echo ""
success "Extraction completed successfully."
else
echo ""
error "Failed to extract the AppImage."
read -p "$(echo -e "${CYAN}Press Enter to exit...${NC}")"
exit 1
fi
# Create desktop shortcut
DESKTOP_FILE="$HOME/.local/share/applications/cursor.desktop"
echo ""
if [ -f "$DESKTOP_FILE" ]; then
warning "Desktop shortcut already exists."
echo -e "${YELLOW}┌─ Shortcut Options ─────────────────────────┐${NC}"
echo -e "${YELLOW}│${NC} Do you want to update the existing shortcut?"
echo -e "${YELLOW}└─────────────────────────────────────────┘${NC}"
read -p "$(echo -e "${CYAN}Update shortcut? (y/n):${NC} ")" update_shortcut
if [[ "$update_shortcut" != "y" && "$update_shortcut" != "Y" ]]; then
info "Skipping shortcut update."
else
info "${UNDERLINE}Creating/updating desktop shortcut...${NC}"
# Use downloaded icon or ask for custom path if download failed
if [[ -z "$ICON_PATH" || ! -f "$ICON_PATH" ]]; then
# Ask for icon path
echo -e "${PURPLE}┌─ Icon Selection ─────────────────────────┐${NC}"
echo -e "${PURPLE}│${NC} Enter a custom path or use the default icon"
echo -e "${PURPLE}└─────────────────────────────────────────┘${NC}"
read -p "$(echo -e "${CYAN}Icon path (or press Enter for default):${NC} ")" user_icon_path
# Use default icon if none provided
if [[ -z "$user_icon_path" ]]; then
icon_path="$EXTRACT_DIR/resources/app/resources/app.asar.unpacked/static/icon.png"
info "Using default icon: $icon_path"
else
icon_path="$user_icon_path"
fi
else
icon_path="$ICON_PATH"
info "Using downloaded icon: $icon_path"
fi
# Create desktop entry file
cat > "$DESKTOP_FILE" << EOF
[Desktop Entry]
Name=Cursor
Comment=Cursor Code Editor
Exec=$EXTRACT_DIR/AppRun --no-sandbox
Icon=$icon_path
Terminal=false
Type=Application
Categories=Development;IDE;
StartupWMClass=Cursor
EOF
# Make desktop file executable
chmod +x "$DESKTOP_FILE"
success "Desktop shortcut updated successfully!"
fi
else
info "${UNDERLINE}Creating desktop shortcut...${NC}"
# Use downloaded icon or ask for custom path if download failed
if [[ -z "$ICON_PATH" || ! -f "$ICON_PATH" ]]; then
# Ask for icon path
echo -e "${PURPLE}┌─ Icon Selection ─────────────────────────┐${NC}"
echo -e "${PURPLE}│${NC} Enter a custom path or use the default icon"
echo -e "${PURPLE}└─────────────────────────────────────────┘${NC}"
read -p "$(echo -e "${CYAN}Icon path (or press Enter for default):${NC} ")" user_icon_path
# Use default icon if none provided
if [[ -z "$user_icon_path" ]]; then
icon_path="$EXTRACT_DIR/resources/app/resources/app.asar.unpacked/static/icon.png"
info "Using default icon: $icon_path"
else
icon_path="$user_icon_path"
fi
else
icon_path="$ICON_PATH"
info "Using downloaded icon: $icon_path"
fi
# Create desktop entry file
cat > "$DESKTOP_FILE" << EOF
[Desktop Entry]
Name=Cursor
Comment=Cursor Code Editor
Exec=$EXTRACT_DIR/AppRun --no-sandbox
Icon=$icon_path
Terminal=false
Type=Application
Categories=Development;IDE;
StartupWMClass=Cursor
EOF
# Make desktop file executable
chmod +x "$DESKTOP_FILE"
success "Desktop shortcut created successfully!"
fi
# Final completion message with centered version number
version_display="v$version"
padding=$(( (49 - ${#version_display} - 4) / 2 ))
left_spaces=$(printf '%*s' $padding '')
right_spaces=$(printf '%*s' $(( 49 - ${#version_display} - 4 - padding )) '')
echo ""
echo -e "${GREEN}╔═══════════════════════════════════════════════════╗${NC}"
echo -e "${GREEN}║ ║${NC}"
echo -e "${GREEN}║${BOLD} ✓ CURSOR UPDATED SUCCESSFULLY TO $left_spaces$version_display$right_spaces${NC}${GREEN} ║${NC}"
echo -e "${GREEN}║ ║${NC}"
echo -e "${GREEN}╚═══════════════════════════════════════════════════╝${NC}"
echo ""
read -p "$(echo -e "${CYAN}Press Enter to exit...${NC}")"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment