Skip to content

Instantly share code, notes, and snippets.

@moosavimaleki
Last active October 6, 2025 09:01
Show Gist options
  • Save moosavimaleki/c855164235bbe4fc31d295b2b535ac29 to your computer and use it in GitHub Desktop.
Save moosavimaleki/c855164235bbe4fc31d295b2b535ac29 to your computer and use it in GitHub Desktop.
AppImage Desktop Installer / نصب‌کننده دسکتاپ برای AppImage
#!/bin/bash
# Generic AppImage Desktop Entry Installer
# Creates desktop entries for any AppImage file
# Usage: ./appimage-desktop-installer.sh /path/to/application.AppImage [AppName] [Category]
set -e
# Color codes for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Function to print colored messages
print_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
print_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
print_error() { echo -e "${RED}[ERROR]${NC} $1"; }
print_debug() { echo -e "${BLUE}[DEBUG]${NC} $1"; }
# Function to show usage
show_usage() {
cat << EOF
Usage: $0 <AppImage-Path> [AppName] [Category]
Arguments:
AppImage-Path : Full path to the AppImage file (required)
AppName : Name for the application (optional, auto-detected from filename)
Category : Desktop category (optional, default: Utility)
Common categories: Audio, Video, Development, Education,
Game, Graphics, Network, Office, Science, Settings, System, Utility
Examples:
$0 ~/Downloads/Obsidian.AppImage
$0 ~/Programs/MyApp.AppImage "My Application" "Development"
$0 ./app.AppImage "Cool App" "Graphics"
EOF
exit 1
}
# Check if help is requested
if [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]] || [[ -z "$1" ]]; then
show_usage
fi
# Get AppImage path
APPIMAGE_PATH="$1"
# Validate AppImage exists
if [[ ! -f "$APPIMAGE_PATH" ]]; then
print_error "AppImage file not found: $APPIMAGE_PATH"
exit 1
fi
# Get absolute path
APPIMAGE_PATH=$(realpath "$APPIMAGE_PATH")
# Make sure AppImage is executable
if [[ ! -x "$APPIMAGE_PATH" ]]; then
print_warn "AppImage is not executable, making it executable..."
chmod +x "$APPIMAGE_PATH"
fi
# Extract filename without extension for default app name
FILENAME=$(basename "$APPIMAGE_PATH" .AppImage)
# Get app name (use provided or extract from filename)
if [[ -n "$2" ]]; then
APP_NAME="$2"
else
APP_NAME="$FILENAME"
fi
# Get category (use provided or default to Utility)
if [[ -n "$3" ]]; then
CATEGORY="$3"
else
CATEGORY="Utility"
fi
# Generate lowercase ID for files
APP_ID=$(echo "$APP_NAME" | tr '[:upper:]' '[:lower:]' | tr ' ' '-')
# Set up directories
ICON_DIR="$HOME/.local/share/icons"
DESKTOP_DIR="$HOME/.local/share/applications"
TMP_EXTRACT="/tmp/appimage-extract-$$"
print_info "Starting desktop entry installation for: $APP_NAME"
print_info "AppImage: $APPIMAGE_PATH"
# Step 1: Test AppImage execution to detect issues
print_info "Step 1: Testing AppImage for compatibility issues..."
# Known GUI applications (Electron-based and others)
KNOWN_GUI_APPS="obsidian|vscode|code|discord|slack|atom|postman|insomnia|gimp|inkscape|blender|krita|kdenlive|audacity"
# Test if AppImage needs --no-sandbox flag
TEST_OUTPUT=$(timeout 5 "$APPIMAGE_PATH" --version 2>&1 || true)
NEEDS_NO_SANDBOX=false
NEEDS_TERMINAL=false
if echo "$TEST_OUTPUT" | grep -qi "SUID sandbox\|sandbox.*not configured\|sandboxing"; then
print_warn " Detected sandbox issue - will use --no-sandbox flag"
NEEDS_NO_SANDBOX=true
fi
# Check if this is a known GUI app (don't rely on --version output)
APP_NAME_LOWER=$(echo "$APP_NAME" | tr '[:upper:]' '[:lower:]')
if echo "$APP_NAME_LOWER" | grep -qiE "$KNOWN_GUI_APPS"; then
print_debug " Recognized as known GUI application"
NEEDS_TERMINAL=false
else
# Only check CLI detection for unknown apps
if echo "$TEST_OUTPUT" | grep -qiE "^[A-Za-z0-9_-]+:.*version|v[0-9]|[0-9]+\.[0-9]" && ! echo "$TEST_OUTPUT" | grep -qi "error\|fatal\|sandbox"; then
print_debug " App appears to be CLI-based"
NEEDS_TERMINAL=true
fi
fi
# Step 2: Extract AppImage to find icon
print_info "Step 2: Extracting AppImage to locate icon..."
mkdir -p "$TMP_EXTRACT"
cd "$TMP_EXTRACT"
if ! "$APPIMAGE_PATH" --appimage-extract > /dev/null 2>&1; then
print_error "Failed to extract AppImage. Make sure it's a valid AppImage file."
rm -rf "$TMP_EXTRACT"
exit 1
fi
print_info " ✓ AppImage extracted successfully"
# Step 3: Detect Terminal requirement from desktop file
print_info "Step 3: Analyzing application metadata..."
# Check if there's an existing desktop file
EXISTING_DESKTOP=$(find squashfs-root -name "*.desktop" | head -1)
if [[ -n "$EXISTING_DESKTOP" ]]; then
print_debug " Found existing desktop file: $EXISTING_DESKTOP"
# Check Terminal setting - THIS HAS PRIORITY!
if grep -q "^Terminal=true" "$EXISTING_DESKTOP"; then
NEEDS_TERMINAL=true
print_debug " Desktop file indicates Terminal=true (overriding detection)"
elif grep -q "^Terminal=false" "$EXISTING_DESKTOP"; then
NEEDS_TERMINAL=false
print_debug " Desktop file indicates Terminal=false (overriding detection)"
fi
# Extract comment if available
COMMENT=$(grep "^Comment=" "$EXISTING_DESKTOP" | cut -d'=' -f2- | head -1)
if [[ -z "$COMMENT" ]]; then
COMMENT="$APP_NAME - Installed via AppImage"
fi
# Extract categories if available
EXISTING_CATEGORIES=$(grep "^Categories=" "$EXISTING_DESKTOP" | cut -d'=' -f2- | head -1)
if [[ -n "$EXISTING_CATEGORIES" ]] && [[ "$CATEGORY" == "Utility" ]]; then
CATEGORY="$EXISTING_CATEGORIES"
print_debug " Using categories from desktop file: $CATEGORY"
fi
else
COMMENT="$APP_NAME - Installed via AppImage"
fi
# Step 4: Find the best icon
print_info "Step 4: Searching for application icon..."
# Search for icons in common locations
ICON_FILE=""
for size in 512 256 128 scalable; do
if [[ "$size" == "scalable" ]]; then
FOUND_ICON=$(find squashfs-root -name "*.svg" -o -name "*.png" | grep -iE "(icon|logo)" | head -1)
else
FOUND_ICON=$(find squashfs-root -path "*/${size}x${size}/*" -name "*.png" | head -1)
fi
if [[ -n "$FOUND_ICON" ]]; then
ICON_FILE="$FOUND_ICON"
break
fi
done
# Fallback: search for any PNG or SVG in common locations
if [[ -z "$ICON_FILE" ]]; then
ICON_FILE=$(find squashfs-root -name "*.png" -o -name "*.svg" | grep -iE "(icon|logo)" | head -1)
fi
# Last resort: any PNG file
if [[ -z "$ICON_FILE" ]]; then
ICON_FILE=$(find squashfs-root -name "*.png" | head -1)
fi
if [[ -z "$ICON_FILE" ]]; then
print_warn "No icon found, desktop entry will be created without icon"
ICON_PATH=""
else
ICON_EXT="${ICON_FILE##*.}"
ICON_PATH="$ICON_DIR/${APP_ID}.${ICON_EXT}"
mkdir -p "$ICON_DIR"
cp "$ICON_FILE" "$ICON_PATH"
print_info " ✓ Icon copied to $ICON_PATH"
fi
# Step 5: Find executable name for StartupWMClass
EXEC_NAME=$(basename "$(find squashfs-root -type f -executable | grep -v '\.so' | head -1)" 2>/dev/null || echo "$APP_ID")
print_info " ✓ Detected executable name: $EXEC_NAME"
# Step 6: Determine final Exec command
if [[ "$NEEDS_NO_SANDBOX" == true ]]; then
EXEC_CMD="$APPIMAGE_PATH --no-sandbox %U"
print_info " ✓ Using --no-sandbox flag for compatibility"
else
EXEC_CMD="$APPIMAGE_PATH %U"
fi
# Step 7: Create desktop entry file
print_info "Step 5: Creating desktop entry file..."
mkdir -p "$DESKTOP_DIR"
DESKTOP_FILE="$DESKTOP_DIR/${APP_ID}.desktop"
# Determine Terminal value
if [[ "$NEEDS_TERMINAL" == true ]]; then
TERMINAL_VALUE="true"
print_info " ✓ Setting Terminal=true (CLI application detected)"
else
TERMINAL_VALUE="false"
print_info " ✓ Setting Terminal=false (GUI application detected)"
fi
cat > "$DESKTOP_FILE" << EOF
[Desktop Entry]
Type=Application
Name=$APP_NAME
Comment=$COMMENT
Exec=$EXEC_CMD
Icon=$ICON_PATH
Terminal=$TERMINAL_VALUE
Categories=$CATEGORY;
StartupWMClass=$EXEC_NAME
X-AppImage-Version=1.0
X-AppImage-Path=$APPIMAGE_PATH
EOF
print_info " ✓ Desktop file created at $DESKTOP_FILE"
# Step 8: Make desktop file executable
print_info "Step 6: Setting permissions..."
chmod +x "$DESKTOP_FILE"
print_info " ✓ Desktop file made executable"
# Step 9: Update desktop database
print_info "Step 7: Updating desktop database..."
if command -v update-desktop-database &> /dev/null; then
update-desktop-database "$DESKTOP_DIR" 2>/dev/null || true
print_info " ✓ Desktop database updated"
else
print_warn " update-desktop-database not available, changes may require logout"
fi
# Cleanup
print_info "Step 8: Cleaning up temporary files..."
cd "$HOME"
rm -rf "$TMP_EXTRACT"
print_info " ✓ Cleanup completed"
echo ""
print_info "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
print_info "✓ Installation completed successfully!"
print_info "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "Application: $APP_NAME"
echo "Desktop File: $DESKTOP_FILE"
echo "Icon: ${ICON_PATH:-Not available}"
echo "Terminal: $TERMINAL_VALUE"
if [[ "$NEEDS_NO_SANDBOX" == true ]]; then
echo "Sandbox: Disabled (--no-sandbox)"
fi
echo ""
print_info "$APP_NAME should now appear in your application menu."
print_warn "If it doesn't appear immediately, try logging out and back in."
echo ""
# Final validation
if [[ "$NEEDS_NO_SANDBOX" == true ]]; then
print_warn "Note: This AppImage requires --no-sandbox flag due to sandbox configuration issues."
print_warn "This is common with Electron-based apps (like Obsidian, VSCode, etc.)"
fi

AppImage Desktop Installer / نصب‌کننده دسکتاپ برای AppImage


📦 What is this?

A universal bash script that automatically creates desktop entries for any AppImage file on Linux. No more manual .desktop file creation!

  1. Download the script:
wget https://gist.githubusercontent.com/[YOUR-GIST-URL]/appimage-desktop-installer.sh
chmod +x appimage-desktop-installer.sh
  1. Run it:
# Basic usage (auto-detect name from filename)
./appimage-desktop-installer.sh ~/Downloads/MyApp.AppImage

# Specify custom name
./appimage-desktop-installer.sh ~/Downloads/MyApp.AppImage "My Application"

# Specify name and category
./appimage-desktop-installer.sh ~/Downloads/MyApp.AppImage "My Application" "Development"

📖 Usage

./appimage-desktop-installer.sh <AppImage-Path> [AppName] [Category]

Arguments:
  AppImage-Path  : Full path to the AppImage file (required)
  AppName        : Name for the application (optional, auto-detected from filename)
  Category       : Desktop category (optional, default: Utility)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment