Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save Malaeu/804b4f0339604e72bb94b3c2e7ed6063 to your computer and use it in GitHub Desktop.
Save Malaeu/804b4f0339604e72bb94b3c2e7ed6063 to your computer and use it in GitHub Desktop.
Automate the integration of Cursor .AppImage on Linux: updates .desktop, manages icons, ensures the latest version, and configures AppArmor (Required for Ubuntu 24.04).
#!/bin/bash
# Temporary comment to avoid abrupt exit
# set -euo pipefail
# Definition of colors for terminal output
readonly RED_COLOR="\e[31m"
readonly GREEN_COLOR="\e[32m"
readonly YELLOW_COLOR="\e[33m"
readonly BLUE_COLOR="\e[34m"
readonly RESET_COLOR="\e[0m"
# General settings
readonly DOWNLOAD_APP_DIR="$HOME/Downloads/.AppImage"
readonly ICON_DIR="$HOME/.local/share/icons"
readonly ICON_URL="https://www.cursor.com/brand/icon.svg"
readonly DESKTOP_FILE_PATH="$HOME/.local/share/applications/cursor.desktop"
readonly APPARMOR_PROFILE="/etc/apparmor.d/cursor-appimage"
readonly DOWNLOAD_URL="https://downloader.cursor.sh/linux/appImage/x64"
readonly TEMP_DIR="/tmp/AppImage"
cursor_appimage_path=""
cursor_icon_path=""
# Support function to print steps
print_step() {
echo -e "${YELLOW_COLOR}$1${RESET_COLOR}"
}
# Support function to print success
print_success() {
echo -e "${GREEN_COLOR}$1${RESET_COLOR}"
}
# Support function for error handling
error_exit() {
echo -e "${RED_COLOR}$1${RESET_COLOR}" >&2
exit 1
}
# Check necessary dependencies
check_dependencies() {
print_step "Checking necessary dependencies..."
for cmd in curl chmod find mkdir wget; do
if ! command -v "$cmd" >/dev/null 2>&1; then
error_exit "This script requires $cmd, but it is not installed.\nPlease install it with 'sudo apt install $cmd' and try again."
fi
done
print_success "All necessary dependencies are installed."
}
# Function to get the latest version of the AppImage in the local directory
get_latest_local_version() {
local appimage_files
mapfile -t appimage_files < <(find "$DOWNLOAD_APP_DIR" -name 'cursor-*.AppImage' -print0 | xargs -0 ls -t)
if [ "${#appimage_files[@]}" -eq 0 ]; then
error_exit "No cursor.sh AppImage files found in $DOWNLOAD_APP_DIR"
fi
echo "${appimage_files[0]}"
}
# Search for the most recent AppImage file path
fetch_app_path() {
print_step "Searching for the most recent AppImage path of cursor.sh"
local appimage_files
mapfile -t appimage_files < <(find "$DOWNLOAD_APP_DIR" -name "cursor-*.AppImage" -printf '%T@ %p\n' | sort -rn | cut -d' ' -f2-)
if [ "${#appimage_files[@]}" -eq 0 ]; then
echo -e "${RED_COLOR}No cursor.sh AppImage files found in $DOWNLOAD_APP_DIR/${RESET_COLOR}"
error_exit "You need to download the latest version first (OPTION 1) and run this script again."
fi
cursor_appimage_path=${appimage_files[0]}
print_success "Path found: $cursor_appimage_path"
}
# Download the application icon
download_logo() {
print_step "Downloading the logo"
mkdir -p "$ICON_DIR"
if ! curl -s -o "$ICON_DIR/cursor-icon.svg" "$ICON_URL"; then
error_exit "Failed to download the logo from $ICON_URL"
fi
print_success "Logo downloaded: $ICON_DIR/cursor-icon.svg"
}
# Create the .desktop file for the application
create_desktop_file() {
print_step "Creating the .desktop file"
cat <<-EOF >"$DESKTOP_FILE_PATH"
[Desktop Entry]
Name=Cursor
Type=Application
Name=Cursor
Exec=$cursor_appimage_path
Icon=$cursor_icon_path
Categories=Utility;Development
Terminal=false
StartupWMClass=Cursor
X-AppImage-Version=latest
Comment=Cursor is an AI-first coding environment.
MimeType=x-scheme-handler/cursor;
EOF
if ! chmod +x "$DESKTOP_FILE_PATH"; then
error_exit "Failed to make the .desktop file executable"
fi
print_success "Desktop file created: $DESKTOP_FILE_PATH"
}
# Configure the AppArmor profile for the application
create_apparmor_profile() {
local appimage_path=$1
if [[ -z $appimage_path ]]; then
appimage_path=$(get_latest_local_version)
fi
print_step "Configuring the AppArmor profile..."
cat <<-EOF | sudo tee $APPARMOR_PROFILE > /dev/null
# This profile allows everything and only exists to give the
# application a name instead of having the label "unconfined"
abi <abi/4.0>,
include <tunables/global>
profile cursor $appimage_path flags=(unconfined) {
userns,
# Site-specific additions and overrides. See local/README for details.
include if exists <local/cursor>
}
EOF
sudo apparmor_parser -r $APPARMOR_PROFILE || error_exit "Failed to apply the AppArmor profile."
print_success "AppArmor profile successfully configured for $appimage_path"
}
make_executable() {
print_step "Making the .desktop file executable"
if ! chmod +x "$DESKTOP_FILE_PATH"; then
error_exit "Failed to make the .desktop file executable"
fi
print_success "Desktop file is now executable."
}
# Main function that checks and downloads the latest version if it is not yet locally present
check_and_download_version() {
local latest_local_version=$(get_latest_local_version)
local latest_local_filename=$(basename "$latest_local_version")
echo -e "${BLUE_COLOR}Latest local version: $(basename "$latest_local_version")${RESET_COLOR}"
# Ensures that the temporary directory exists
mkdir -p "$TEMP_DIR"
# cd "$TEMP_DIR" || exit
# Starts a download with wget in a temporary directory
wget -q --content-disposition --trust-server-names --directory-prefix="$TEMP_DIR" "$DOWNLOAD_URL" &
local wget_pid=$!
# Waits a few seconds to allow the connection to establish and the header to be read
echo -e "${YELLOW_COLOR}Searching for the latest version. Please wait...${RESET_COLOR}"
sleep 5
# Kills the wget process after obtaining the file name
kill $wget_pid 2>/dev/null
wait $wget_pid 2>/dev/null
# Finds the file name that would start to be downloaded
local temp_online_filename=$(ls -t "$TEMP_DIR"/* | head -n 1)
local online_filename=$(basename "$temp_online_filename")
# Compares versions and downloads if necessary
if [[ "$latest_local_filename" == "$online_filename" ]]; then
echo -e "${GREEN_COLOR}The latest version ($online_filename) is already downloaded.${RESET_COLOR}"
rm -f "$TEMP_DIR/$temp_online_filename" # Remove the partial file if it was created
else
echo -e "${BLUE_COLOR}A new version is available: $online_filename${RESET_COLOR}"
cd "$DOWNLOAD_APP_DIR" || exit
wget --quiet --show-progress --content-disposition -P "$DOWNLOAD_APP_DIR" --trust-server-names "$DOWNLOAD_URL"
echo -e "${GREEN_COLOR}Download completed: $DOWNLOAD_APP_DIR/$online_filename${RESET_COLOR}"
fi
# Cleans up the temporary directory
rm -rf "$TEMP_DIR"
}
# Function to search and prepare the AppImage
fetch_and_prepare_appimage() {
check_dependencies
fetch_app_path
download_logo
create_desktop_file
make_executable
create_apparmor_profile
echo -e "${GREEN_COLOR}Application installed and ready to use.${RESET_COLOR}"
}
# Function to create the AppArmor profile
setup_apparmor_profile() {
check_dependencies
fetch_app_path
create_apparmor_profile
}
# Interactive menu
PS3='Please choose an option: '
options=(
"Fetch the latest version and download (if not already present)"
"Re/Install the latest downloaded version"
"Just configure AppArmor (Required for Ubuntu 24.04)"
"Exit"
)
select opt in "${options[@]}"
do
case $opt in
"Fetch the latest version and download (if not already present)")
check_and_download_version
;;
"Re/Install the latest downloaded version")
fetch_and_prepare_appimage
;;
"Just configure AppArmor (Required for Ubuntu 24.04)")
setup_apparmor_profile
;;
"Exit")
break
;;
*) echo "Invalid option $REPLY";;
esac
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment