🐻 feat(install-cursor): Add script to install Cursor AI (#41)

This commit adds a new script `run.sh` to the `install-cursor` directory. The script
automates the process of downloading and installing the Cursor AI application for
various platforms (macOS, Linux, and Windows).

The key changes include:

- Fetching the latest version information from a JSON file
- Detecting the user's operating system and architecture
- Downloading the appropriate binary for the detected platform
- Verifying the binary's signature and notarization status on macOS
- Installing the application on the user's system

This script aims to simplify the installation process and ensure a smooth user
experience when setting up the Cursor AI application.
This commit is contained in:
Christopher
2025-05-13 19:33:49 -05:00
committed by GitHub
parent e6667ea933
commit 8cb380870a
2 changed files with 301 additions and 0 deletions

135
install-cursor/README.md Normal file
View File

@@ -0,0 +1,135 @@
# Install Cursor (Cross-Platform Installer)
Easily download and install any version of [Cursor](https://www.cursor.com/) on **macOS**, **Linux**, or **Windows** from the command line.
This script is part of the [big-bear-scripts](https://github.com/bigbeartechworld/big-bear-scripts) collection.
---
## Features
- **Interactive version picker** choose any available Cursor version, or install the latest by default.
- **Cross-platform** supports macOS (Intel & Apple Silicon), Linux (x86_64 & ARM64), and Windows.
- **Security checks** verifies download sources and (on macOS) checks app signature and notarization.
- **Automatic installation** handles mounting, copying, and permissions for you.
- **Safe cleanup** removes temporary files after installation.
---
## Quick Start
Run the installer directly from your terminal:
```bash
bash -c "$(wget -qLO - https://raw.githubusercontent.com/bigbeartechworld/big-bear-scripts/master/install-cursor/run.sh)"
```
Or, using `curl`:
```bash
bash -c "$(curl -fsSL https://raw.githubusercontent.com/bigbeartechworld/big-bear-scripts/master/install-cursor/run.sh)"
```
---
## Prerequisites
- **curl** (for downloading files)
- **jq** (for parsing JSON)
- **wget** (optional, for the quick start command)
- **macOS only:**
- `hdiutil`, `codesign`, `spctl`, and `sudo` (for DMG mounting, signature, and notarization checks)
---
## How It Works
1. **Fetches** the latest Cursor version list from the official source.
2. **Prompts** you to pick a version (or defaults to the latest).
3. **Detects** your platform and downloads the correct binary.
4. **Verifies** the download source for safety.
5. **Installs** Cursor:
- **macOS:** Mounts DMG, verifies signature, installs to `/Applications`.
- **Linux:** Saves AppImage to `~/Applications/`.
- **Windows:** Saves installer to `~/Downloads/` (run manually).
6. **Cleans up** all temporary files.
---
## Example Output
```
📥 Fetching version list...
🧭 Available Cursor Versions:
1) Latest (default)
2) 0.22.2
3) 0.22.1
#? 1
🆕 Using latest version: 0.22.2
🔗 Downloading: https://downloads.cursor.com/...
💿 Mounting DMG...
🔍 Verifying app signature...
✅ App is properly signed and verified.
🛡️ Checking notarization status with spctl...
✅ App is notarized and passes Gatekeeper checks.
🔐 Requesting admin rights to install to /Applications...
✅ Installed Cursor 0.22.2 to /Applications
🎉 Done!
```
---
## Security
- **Download host and JSON source are validated** before proceeding.
- **macOS:** App signature and notarization are checked for extra safety.
- **Linux/Windows:** No signature check; always verify the source if you have concerns.
---
## Testing Status
| Platform | Testing Status |
| -------- | -------------- |
| macOS | Fully Tested |
| Windows | Not Tested |
| Linux | Not Tested |
---
## Troubleshooting
- **Missing dependencies?**
Install them with:
- macOS: `brew install jq`
- Linux: `sudo apt-get install jq`
- **Permission denied?**
On macOS, you may be prompted for your password to install to `/Applications`.
- **Unsupported OS or architecture?**
The script will exit with a clear error message.
If you encounter any issues, particularly on Windows or Linux, please let us know by opening an issue or visiting the community forum.
---
## License
MIT License
---
## Credits
Script by [BigBearTechWorld](https://github.com/bigbeartechworld) community contributors.
Special thanks to the [oslook/cursor-ai-downloads](https://github.com/oslook/cursor-ai-downloads) repository for providing the Cursor version history data.
Not affiliated with Cursor.
---
## Support
If this script is helpful to you, consider supporting the project on [Ko-fi](https://ko-fi.com/bigbeartechworld)!
---
**Enjoy your new Cursor install!**
For issues, suggestions, or support, please visit the [BigBearTechWorld Community Forum](https://community.bigbeartechworld.com) or open an issue in the [big-bear-scripts repo](https://github.com/bigbeartechworld/big-bear-scripts/issues).

166
install-cursor/run.sh Normal file
View File

@@ -0,0 +1,166 @@
#!/usr/bin/env bash
set -e
# === Constants ===
JSON_URL="https://raw.githubusercontent.com/oslook/cursor-ai-downloads/refs/heads/main/version-history.json"
EXPECTED_JSON_HOST="raw.githubusercontent.com"
EXPECTED_DOWNLOAD_HOST="downloads.cursor.com"
TMP_DIR=$(mktemp -d)
# === Validate JSON host ===
JSON_HOST=$(echo "$JSON_URL" | awk -F/ '{print $3}')
if [ "$JSON_HOST" != "$EXPECTED_JSON_HOST" ]; then
echo "❌ Error: Unexpected JSON host: $JSON_HOST"
exit 1
fi
# === Download JSON ===
echo "📥 Fetching version list..."
curl -fsSL "$JSON_URL" -o "$TMP_DIR/version-history.json"
# === Show versions and prompt ===
echo "🧭 Available Cursor Versions:"
VERSIONS=$(jq -r '.versions[].version' "$TMP_DIR/version-history.json")
VERSION_ARRAY=($VERSIONS) # convert to bash array
# Insert "Latest (default)" at the top visually
PS3=$'\n#? '
select USER_CHOICE in "Latest (default)" "${VERSION_ARRAY[@]}"; do
if [[ "$REPLY" == "1" || -z "$REPLY" ]]; then
VERSION="${VERSION_ARRAY[0]}"
echo "🆕 Using latest version: $VERSION"
break
elif [[ "$REPLY" =~ ^[0-9]+$ && "$REPLY" -le $((${#VERSION_ARRAY[@]} + 1)) ]]; then
VERSION="${VERSION_ARRAY[$((REPLY-2))]}"
echo "📦 Using version: $VERSION"
break
else
echo "❌ Invalid selection. Try again."
fi
done
# === Detect platform ===
UNAME_OUT="$(uname -s)"
ARCH_OUT="$(uname -m)"
OS=""
PLATFORM=""
case "$UNAME_OUT" in
Darwin)
OS="macos"
case "$ARCH_OUT" in
arm64) PLATFORM="darwin-arm64" ;;
x86_64) PLATFORM="darwin-x64" ;;
*) PLATFORM="darwin-universal" ;;
esac
;;
Linux)
OS="linux"
case "$ARCH_OUT" in
x86_64) PLATFORM="linux-x64" ;;
aarch64|arm64) PLATFORM="linux-arm64" ;;
*) echo "❌ Unsupported Linux architecture: $ARCH_OUT"; exit 1 ;;
esac
;;
MINGW*|MSYS*|CYGWIN*)
OS="windows"
case "$ARCH_OUT" in
x86_64) PLATFORM="win32-x64-user" ;;
aarch64|arm64) PLATFORM="win32-arm64-user" ;;
*) echo "❌ Unsupported Windows architecture: $ARCH_OUT"; exit 1 ;;
esac
;;
*)
echo "❌ Unsupported OS: $UNAME_OUT"
exit 1
;;
esac
# === Extract URL ===
VERSION_INDEX=$(jq ".versions | map(.version == \"$VERSION\") | index(true)" "$TMP_DIR/version-history.json")
DOWNLOAD_URL=$(jq -r ".versions[$VERSION_INDEX].platforms[\"$PLATFORM\"]" "$TMP_DIR/version-history.json")
if [[ -z "$DOWNLOAD_URL" || "$DOWNLOAD_URL" == "null" ]]; then
echo "❌ No binary for $PLATFORM in version $VERSION"
exit 1
fi
DOWNLOAD_HOST=$(echo "$DOWNLOAD_URL" | awk -F/ '{print $3}')
if [ "$DOWNLOAD_HOST" != "$EXPECTED_DOWNLOAD_HOST" ]; then
echo "❌ Untrusted host: $DOWNLOAD_HOST"
exit 1
fi
echo "🔗 Downloading: $DOWNLOAD_URL"
# === Download binary ===
FILENAME="${TMP_DIR}/cursor_download"
curl -fsSL "$DOWNLOAD_URL" -o "$FILENAME"
# === Install ===
if [ "$OS" == "macos" ]; then
echo "💿 Mounting DMG..."
MOUNT_POINT="/Volumes/CursorInstaller"
hdiutil attach "$FILENAME" -mountpoint "$MOUNT_POINT" -nobrowse -quiet
APP_PATH=$(find "$MOUNT_POINT" -name "*.app" | head -n 1)
echo "🔍 Verifying app signature..."
codesign --verify --deep --strict --verbose=2 "$APP_PATH"
if [ $? -ne 0 ]; then
echo "❌ Signature verification failed. Aborting install."
hdiutil detach "$MOUNT_POINT" -quiet
exit 1
else
echo "✅ App is properly signed and verified."
fi
echo "🛡️ Checking notarization status with spctl..."
spctl --assess --type execute --verbose=4 "$APP_PATH"
if [ $? -ne 0 ]; then
echo "⚠️ Warning: App failed notarization check (spctl). It may still run, but could be blocked by Gatekeeper."
else
echo "✅ App is notarized and passes Gatekeeper checks."
fi
if [ -z "$APP_PATH" ]; then
echo "❌ .app not found"
hdiutil detach "$MOUNT_POINT" -quiet
exit 1
fi
echo "🔐 Requesting admin rights to install to /Applications..."
echo "🧼 Cleaning up existing installation (if any)..."
sudo rm -rf "/Applications/$(basename "$APP_PATH")"
echo "📦 Installing with ditto..."
sudo ditto "$APP_PATH" "/Applications/$(basename "$APP_PATH")"
if [ -d "/Applications/$(basename "$APP_PATH")" ]; then
echo "✅ Installed Cursor $VERSION to /Applications"
else
hdiutil detach "$MOUNT_POINT" -quiet
echo "❌ Something went wrong — installation incomplete."
exit 1
fi
hdiutil detach "$MOUNT_POINT" -quiet
echo "✅ Installed Cursor $VERSION to /Applications"
elif [ "$OS" == "linux" ]; then
mkdir -p "$HOME/Applications"
FINAL_PATH="$HOME/Applications/Cursor-${VERSION}.AppImage"
mv "$FILENAME" "$FINAL_PATH"
chmod +x "$FINAL_PATH"
echo "✅ Saved AppImage to $FINAL_PATH"
elif [ "$OS" == "windows" ]; then
FINAL_PATH="$HOME/Downloads/$(basename "$DOWNLOAD_URL")"
mv "$FILENAME" "$FINAL_PATH"
echo "📥 Saved installer to $FINAL_PATH"
echo "💡 Please run it manually."
else
echo "❌ Unknown platform"
exit 1
fi
# === Cleanup ===
rm -rf "$TMP_DIR"
echo "🎉 Done!"