Last active
April 5, 2026 23:07
-
-
Save denisalevi/b79ee60b915042e061dbd88bb127b72f to your computer and use it in GitHub Desktop.
Script to download and install the latest VSCode and/or Cursor CLI on a remote server (using curl)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # ----------------------------------------------------------------------- | |
| # update-vscode-cli: Download and install VSCode and/or Cursor CLI tools | |
| # ----------------------------------------------------------------------- | |
| # | |
| # This script downloads and installs the latest versions of CLI tools | |
| # for VSCode on Linux systems. | |
| # | |
| # Usage: | |
| # update-vscode-cli # Install/update all supported tools | |
| # update-vscode-cli code # Install/update only VSCode CLI | |
| # update-vscode-cli cursor # Install/update only Cursor CLI | |
| # | |
| # Features: | |
| # - Installs tools in $HOME/.local/bin | |
| # - Maintains versioned binaries (code-1.2.3, cursor-4.5.6) | |
| # - Creates symbolic links to the latest versions (code, cursor) | |
| # - Preserves previous versions | |
| # - Automatically detects installed versions | |
| # ----------------------------------------------------------------------- | |
| # Exit on error, undefined variables, and propagate pipe failures | |
| set -euo pipefail | |
| # Configuration | |
| TEMP_DIR="/tmp/cli-update-$USER" | |
| INSTALL_DIR="$HOME/.local/bin" | |
| # Optional override for download tool selection. Set to 'curl' or 'wget'. | |
| # Can be set via environment, e.g. `DOWNLOAD_TOOL=wget ./update-vscode-cli`. | |
| : "${DOWNLOAD_TOOL:=}" | |
| # Define application configurations with download details | |
| declare -A APPS=( | |
| ["code"]="https://code.visualstudio.com/sha/download?build=stable&os=cli-alpine-x64" | |
| ["cursor"]="https://api2.cursor.sh/updates/download-latest?os=cli-alpine-x64" | |
| ) | |
| # Define output filenames for downloads | |
| declare -A OUTPUT_FILES=( | |
| ["code"]="vscode_cli.tar.gz" | |
| ["cursor"]="cursor_cli.tar.gz" | |
| ) | |
| # Determine which downloader to use (curl preferred, fallback to wget), with optional override | |
| determine_downloader() { | |
| if [ -n "${DOWNLOAD_TOOL:-}" ]; then | |
| case "$DOWNLOAD_TOOL" in | |
| curl|wget) | |
| DOWNLOADER="$DOWNLOAD_TOOL" | |
| ;; | |
| *) | |
| echo "Error: DOWNLOAD_TOOL must be 'curl' or 'wget' if set" >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| else | |
| if command -v curl >/dev/null 2>&1; then | |
| DOWNLOADER="curl" | |
| elif command -v wget >/dev/null 2>&1; then | |
| DOWNLOADER="wget" | |
| else | |
| echo "Error: neither 'curl' nor 'wget' is installed. Please install one." >&2 | |
| exit 1 | |
| fi | |
| fi | |
| echo "Using downloader: $DOWNLOADER" | |
| } | |
| # Download helper using the chosen tool | |
| download_file() { | |
| local url=$1 | |
| local output_file=$2 | |
| case "$DOWNLOADER" in | |
| curl) | |
| curl --fail --location --show-error --output "$output_file" "$url" | |
| ;; | |
| wget) | |
| wget --quiet --output-document="$output_file" "$url" | |
| ;; | |
| *) | |
| echo "Internal error: unknown downloader '$DOWNLOADER'" >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| } | |
| # Function to install a specific CLI | |
| install_cli() { | |
| local BINARY_NAME=$1 | |
| local DOWNLOAD_URL=$2 | |
| local OUTPUT_FILE="${OUTPUT_FILES[$BINARY_NAME]}" | |
| echo "===== Installing $BINARY_NAME CLI =====" | |
| # Create clean temporary directory for this app | |
| local APP_TEMP_DIR="$TEMP_DIR/$BINARY_NAME" | |
| echo "Setting up temporary directory at $APP_TEMP_DIR" | |
| rm -rf "$APP_TEMP_DIR" | |
| mkdir -p "$APP_TEMP_DIR" | |
| cd "$APP_TEMP_DIR" | |
| # Download and extract | |
| echo "Downloading $BINARY_NAME CLI for Linux x64..." | |
| download_file "$DOWNLOAD_URL" "$OUTPUT_FILE" | |
| echo "Extracting $BINARY_NAME binary from $OUTPUT_FILE..." | |
| tar -xzf "$OUTPUT_FILE" | |
| # Ensure the binary exists | |
| if [ ! -f "$BINARY_NAME" ]; then | |
| echo "Error: $BINARY_NAME binary not found after extraction" | |
| # List directory contents to help diagnose the issue | |
| echo "Directory contents:" | |
| ls -la | |
| return 1 | |
| fi | |
| # Get version information | |
| local VERSION=$("./$BINARY_NAME" --version | awk 'NR==1 {print $2}') | |
| local VERSIONED_PATH="$INSTALL_DIR/$BINARY_NAME-$VERSION" | |
| local SYMLINK_PATH="$INSTALL_DIR/$BINARY_NAME" | |
| echo "Detected version: $VERSION" | |
| # Ensure installation directory exists | |
| mkdir -p "$INSTALL_DIR" | |
| # Remove existing symlink if it exists | |
| if [ -L "$SYMLINK_PATH" ]; then | |
| echo "Removing existing symlink at $SYMLINK_PATH" | |
| rm "$SYMLINK_PATH" | |
| elif [ -e "$SYMLINK_PATH" ]; then | |
| echo "Warning: $SYMLINK_PATH exists but is not a symlink. Renaming to $SYMLINK_PATH.bak" | |
| mv "$SYMLINK_PATH" "$SYMLINK_PATH.bak" | |
| fi | |
| # Install the binary | |
| echo "Installing $BINARY_NAME CLI version $VERSION" | |
| if [ -e "$VERSIONED_PATH" ]; then | |
| echo "Version $VERSION already exists at $VERSIONED_PATH. Skipping copy." | |
| else | |
| echo "Copying binary to $VERSIONED_PATH" | |
| cp -v "$BINARY_NAME" "$VERSIONED_PATH" | |
| chmod +x "$VERSIONED_PATH" | |
| fi | |
| # Create symlink | |
| echo "Creating new symbolic link at $SYMLINK_PATH" | |
| ln -sv "$VERSIONED_PATH" "$SYMLINK_PATH" | |
| echo "✅ $BINARY_NAME CLI version $VERSION is now installed in $SYMLINK_PATH!" | |
| echo "If $SYMLINK_PATH is in your PATH, you can now use the '$BINARY_NAME'" | |
| echo "command from anywhere." | |
| echo | |
| } | |
| # Choose downloader before processing any arguments | |
| determine_downloader | |
| # Process command line arguments | |
| if [ $# -eq 0 ]; then | |
| # No arguments, install all apps | |
| for app in "${!APPS[@]}"; do | |
| install_cli "$app" "${APPS[$app]}" | |
| done | |
| else | |
| # Install only specified apps | |
| for app in "$@"; do | |
| if [[ -v APPS[$app] ]]; then | |
| install_cli "$app" "${APPS[$app]}" | |
| else | |
| echo "Error: Unknown application '$app'" | |
| echo "Available options: ${!APPS[@]}" | |
| exit 1 | |
| fi | |
| done | |
| fi | |
| # Cleanup | |
| echo "Cleaning up temporary files" | |
| rm -rf "$TEMP_DIR" | |
| # Success message | |
| echo | |
| echo "All requested CLI tools have been installed to $INSTALL_DIR" | |
| echo "Previous versions (if any) are preserved in $INSTALL_DIR" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment