#!/bin/bash set -euo pipefail # --- Architecture Detection --- echo -e "\033[1;36m--- Architecture Detection ---\033[0m" echo "⚙️ Detecting system architecture..." HOST_ARCH=$(dpkg --print-architecture) echo "Detected host architecture: $HOST_ARCH" cat /etc/os-release && uname -m && dpkg --print-architecture # Set variables based on detected architecture if [ "$HOST_ARCH" = "amd64" ]; then CLAUDE_DOWNLOAD_URL="https://storage.googleapis.com/osprey-downloads-c02f6a0d-347c-492b-a752-3e0651722e97/nest-win-x64/Claude-Setup-x64.exe" ARCHITECTURE="amd64" CLAUDE_EXE_FILENAME="Claude-Setup-x64.exe" echo "Configured for amd64 build." elif [ "$HOST_ARCH" = "arm64" ]; then CLAUDE_DOWNLOAD_URL="https://storage.googleapis.com/osprey-downloads-c02f6a0d-347c-492b-a752-3e0651722e97/nest-win-arm64/Claude-Setup-arm64.exe" ARCHITECTURE="arm64" CLAUDE_EXE_FILENAME="Claude-Setup-arm64.exe" echo "Configured for arm64 build." else echo "❌ Unsupported architecture: $HOST_ARCH. This script currently supports amd64 and arm64." exit 1 fi echo "Target Architecture (detected): $ARCHITECTURE" # Renamed echo echo -e "\033[1;36m--- End Architecture Detection ---\033[0m" if [ ! -f "/etc/debian_version" ]; then echo "❌ This script requires a Debian-based Linux distribution" exit 1 fi if [ "$EUID" -eq 0 ]; then echo "❌ This script should not be run using sudo or as the root user." echo " It will prompt for sudo password when needed for specific actions." echo " Please run as a normal user." exit 1 fi ORIGINAL_USER=$(whoami) ORIGINAL_HOME=$(getent passwd "$ORIGINAL_USER" | cut -d: -f6) if [ -z "$ORIGINAL_HOME" ]; then echo "❌ Could not determine home directory for user $ORIGINAL_USER." exit 1 fi echo "Running as user: $ORIGINAL_USER (Home: $ORIGINAL_HOME)" # Check for NVM and source it if found - this may provide a Node.js 20+ version if [ -d "$ORIGINAL_HOME/.nvm" ]; then echo "Found NVM installation for user $ORIGINAL_USER, checking for Node.js 20+..." export NVM_DIR="$ORIGINAL_HOME/.nvm" if [ -s "$NVM_DIR/nvm.sh" ]; then # Source NVM script to set up NVM environment variables temporarily # shellcheck disable=SC1091 \. "$NVM_DIR/nvm.sh" # This loads nvm # Initialize and find the path to the currently active or default Node version's bin directory NODE_BIN_PATH="" NODE_BIN_PATH=$(nvm which current | xargs dirname 2>/dev/null || find "$NVM_DIR/versions/node" -maxdepth 2 -type d -name 'bin' | sort -V | tail -n 1) if [ -n "$NODE_BIN_PATH" ] && [ -d "$NODE_BIN_PATH" ]; then echo "Adding NVM Node bin path to PATH: $NODE_BIN_PATH" export PATH="$NODE_BIN_PATH:$PATH" else echo "Warning: Could not determine NVM Node bin path." fi else echo "Warning: nvm.sh script not found or not sourceable." fi fi # End of if [ -d "$ORIGINAL_HOME/.nvm" ] check echo "System Information:" echo "Distribution: $(grep "PRETTY_NAME" /etc/os-release | cut -d'"' -f2)" echo "Debian version: $(cat /etc/debian_version)" echo "Target Architecture: $ARCHITECTURE" PACKAGE_NAME="claude-desktop" MAINTAINER="Claude Desktop Linux Maintainers" DESCRIPTION="Claude Desktop for Linux" PROJECT_ROOT="$(pwd)" WORK_DIR="$PROJECT_ROOT/build" APP_STAGING_DIR="$WORK_DIR/electron-app" VERSION="" echo -e "\033[1;36m--- Argument Parsing ---\033[0m" BUILD_FORMAT="deb" CLEANUP_ACTION="yes" TEST_FLAGS_MODE=false while [[ $# -gt 0 ]]; do key="$1" case $key in -b|--build) if [[ -z "$2" || "$2" == -* ]]; then echo "❌ Error: Argument for $1 is missing" >&2; exit 1 fi BUILD_FORMAT="$2" shift 2 ;; # Shift past flag and value -c|--clean) if [[ -z "$2" || "$2" == -* ]]; then echo "❌ Error: Argument for $1 is missing" >&2; exit 1 fi CLEANUP_ACTION="$2" shift 2 ;; # Shift past flag and value --test-flags) TEST_FLAGS_MODE=true shift # past argument ;; -h|--help) echo "Usage: $0 [--build deb|appimage] [--clean yes|no] [--test-flags]" echo " --build: Specify the build format (deb or appimage). Default: deb" echo " --clean: Specify whether to clean intermediate build files (yes or no). Default: yes" echo " --test-flags: Parse flags, print results, and exit without building." exit 0 ;; *) echo "❌ Unknown option: $1" >&2 echo "Use -h or --help for usage information." >&2 exit 1 ;; esac done # Validate arguments BUILD_FORMAT=$(echo "$BUILD_FORMAT" | tr '[:upper:]' '[:lower:]') CLEANUP_ACTION=$(echo "$CLEANUP_ACTION" | tr '[:upper:]' '[:lower:]') if [[ "$BUILD_FORMAT" != "deb" && "$BUILD_FORMAT" != "appimage" ]]; then echo "❌ Invalid build format specified: '$BUILD_FORMAT'. Must be 'deb' or 'appimage'." >&2 exit 1 fi if [[ "$CLEANUP_ACTION" != "yes" && "$CLEANUP_ACTION" != "no" ]]; then echo "❌ Invalid cleanup option specified: '$CLEANUP_ACTION'. Must be 'yes' or 'no'." >&2 exit 1 fi echo "Selected build format: $BUILD_FORMAT" echo "Cleanup intermediate files: $CLEANUP_ACTION" PERFORM_CLEANUP=false if [ "$CLEANUP_ACTION" = "yes" ]; then PERFORM_CLEANUP=true fi echo -e "\033[1;36m--- End Argument Parsing ---\033[0m" # Exit early if --test-flags mode is enabled if [ "$TEST_FLAGS_MODE" = true ]; then echo "--- Test Flags Mode Enabled ---" # Target Architecture is implicitly detected now echo "Build Format: $BUILD_FORMAT" echo "Clean Action: $CLEANUP_ACTION" echo "Exiting without build." exit 0 fi check_command() { if ! command -v "$1" &> /dev/null; then echo "❌ $1 not found" return 1 else echo "✓ $1 found" return 0 fi } echo "Checking dependencies..." DEPS_TO_INSTALL="" COMMON_DEPS="p7zip wget wrestool icotool convert" DEB_DEPS="dpkg-deb" APPIMAGE_DEPS="" ALL_DEPS_TO_CHECK="$COMMON_DEPS" if [ "$BUILD_FORMAT" = "deb" ]; then ALL_DEPS_TO_CHECK="$ALL_DEPS_TO_CHECK $DEB_DEPS" elif [ "$BUILD_FORMAT" = "appimage" ]; then ALL_DEPS_TO_CHECK="$ALL_DEPS_TO_CHECK $APPIMAGE_DEPS" fi for cmd in $ALL_DEPS_TO_CHECK; do if ! check_command "$cmd"; then case "$cmd" in "p7zip") DEPS_TO_INSTALL="$DEPS_TO_INSTALL p7zip-full" ;; "wget") DEPS_TO_INSTALL="$DEPS_TO_INSTALL wget" ;; "wrestool"|"icotool") DEPS_TO_INSTALL="$DEPS_TO_INSTALL icoutils" ;; "convert") DEPS_TO_INSTALL="$DEPS_TO_INSTALL imagemagick" ;; "dpkg-deb") DEPS_TO_INSTALL="$DEPS_TO_INSTALL dpkg-dev" ;; esac fi done if [ -n "$DEPS_TO_INSTALL" ]; then echo "System dependencies needed: $DEPS_TO_INSTALL" echo "Attempting to install using sudo..." if ! sudo -v; then echo "❌ Failed to validate sudo credentials. Please ensure you can run sudo." exit 1 fi if ! sudo apt update; then echo "❌ Failed to run 'sudo apt update'." exit 1 fi # Here on purpose no "" to expand the 'list', thus # shellcheck disable=SC2086 if ! sudo apt install -y $DEPS_TO_INSTALL; then echo "❌ Failed to install dependencies using 'sudo apt install'." exit 1 fi echo "✓ System dependencies installed successfully via sudo." fi rm -rf "$WORK_DIR" mkdir -p "$WORK_DIR" mkdir -p "$APP_STAGING_DIR" echo -e "\033[1;36m--- Node.js Setup ---\033[0m" echo "Checking Node.js version..." NODE_VERSION_OK=false if command -v node &> /dev/null; then NODE_VERSION=$(node --version | cut -d'v' -f2) NODE_MAJOR=$(echo "$NODE_VERSION" | cut -d'.' -f1) echo "System Node.js version: v$NODE_VERSION" if [ "$NODE_MAJOR" -ge 20 ]; then echo "✓ System Node.js version is adequate (v$NODE_VERSION)" NODE_VERSION_OK=true else echo "⚠️ System Node.js version is too old (v$NODE_VERSION). Need v20+" fi else echo "⚠️ Node.js not found in system" fi # If system Node.js is not adequate, install a local copy if [ "$NODE_VERSION_OK" = false ]; then echo "Installing Node.js v20 locally in build directory..." # Determine Node.js download URL based on architecture if [ "$ARCHITECTURE" = "amd64" ]; then NODE_ARCH="x64" elif [ "$ARCHITECTURE" = "arm64" ]; then NODE_ARCH="arm64" else echo "❌ Unsupported architecture for Node.js: $ARCHITECTURE" exit 1 fi NODE_VERSION_TO_INSTALL="20.18.1" NODE_TARBALL="node-v${NODE_VERSION_TO_INSTALL}-linux-${NODE_ARCH}.tar.xz" NODE_URL="https://nodejs.org/dist/v${NODE_VERSION_TO_INSTALL}/${NODE_TARBALL}" NODE_INSTALL_DIR="$WORK_DIR/node" echo "Downloading Node.js v${NODE_VERSION_TO_INSTALL} for ${NODE_ARCH}..." cd "$WORK_DIR" if ! wget -O "$NODE_TARBALL" "$NODE_URL"; then echo "❌ Failed to download Node.js from $NODE_URL" cd "$PROJECT_ROOT" exit 1 fi echo "Extracting Node.js..." if ! tar -xf "$NODE_TARBALL"; then echo "❌ Failed to extract Node.js tarball" cd "$PROJECT_ROOT" exit 1 fi # Move extracted files to a consistent location mv "node-v${NODE_VERSION_TO_INSTALL}-linux-${NODE_ARCH}" "$NODE_INSTALL_DIR" # Add local Node.js to PATH for this script export PATH="$NODE_INSTALL_DIR/bin:$PATH" # Verify local Node.js installation if command -v node &> /dev/null; then LOCAL_NODE_VERSION=$(node --version) echo "✓ Local Node.js installed successfully: $LOCAL_NODE_VERSION" else echo "❌ Failed to install local Node.js" cd "$PROJECT_ROOT" exit 1 fi # Clean up tarball rm -f "$NODE_TARBALL" cd "$PROJECT_ROOT" fi echo -e "\033[1;36m--- End Node.js Setup ---\033[0m" echo -e "\033[1;36m--- Electron & Asar Handling ---\033[0m" CHOSEN_ELECTRON_MODULE_PATH="" ASAR_EXEC="" echo "Ensuring local Electron and Asar installation in $WORK_DIR..." cd "$WORK_DIR" if [ ! -f "package.json" ]; then echo "Creating temporary package.json in $WORK_DIR for local install..." echo '{"name":"claude-desktop-build","version":"0.0.1","private":true}' > package.json fi ELECTRON_DIST_PATH="$WORK_DIR/node_modules/electron/dist" ASAR_BIN_PATH="$WORK_DIR/node_modules/.bin/asar" INSTALL_NEEDED=false if [ ! -d "$ELECTRON_DIST_PATH" ]; then echo "Electron distribution not found." INSTALL_NEEDED=true fi if [ ! -f "$ASAR_BIN_PATH" ]; then echo "Asar binary not found." INSTALL_NEEDED=true fi if [ "$INSTALL_NEEDED" = true ]; then echo "Installing Electron and Asar locally into $WORK_DIR..." if ! npm install --no-save electron @electron/asar; then echo "❌ Failed to install Electron and/or Asar locally." cd "$PROJECT_ROOT" exit 1 fi echo "✓ Electron and Asar installation command finished." else echo "✓ Local Electron distribution and Asar binary already present." fi if [ -d "$ELECTRON_DIST_PATH" ]; then echo "✓ Found Electron distribution directory at $ELECTRON_DIST_PATH." CHOSEN_ELECTRON_MODULE_PATH="$(realpath "$WORK_DIR/node_modules/electron")" echo "✓ Setting Electron module path for copying to $CHOSEN_ELECTRON_MODULE_PATH." else echo "❌ Failed to find Electron distribution directory at '$ELECTRON_DIST_PATH' after installation attempt." echo " Cannot proceed without the Electron distribution files." cd "$PROJECT_ROOT" exit 1 fi if [ -f "$ASAR_BIN_PATH" ]; then ASAR_EXEC="$(realpath "$ASAR_BIN_PATH")" echo "✓ Found local Asar binary at $ASAR_EXEC." else echo "❌ Failed to find Asar binary at '$ASAR_BIN_PATH' after installation attempt." cd "$PROJECT_ROOT" exit 1 fi cd "$PROJECT_ROOT" if [ -z "$CHOSEN_ELECTRON_MODULE_PATH" ] || [ ! -d "$CHOSEN_ELECTRON_MODULE_PATH" ]; then echo "❌ Critical error: Could not resolve a valid Electron module path to copy." exit 1 fi echo "Using Electron module path: $CHOSEN_ELECTRON_MODULE_PATH" echo "Using asar executable: $ASAR_EXEC" echo -e "\033[1;36m--- Download the latest Claude executable ---\033[0m" echo "📥 Downloading Claude Desktop installer for $ARCHITECTURE..." CLAUDE_EXE_PATH="$WORK_DIR/$CLAUDE_EXE_FILENAME" if ! wget -O "$CLAUDE_EXE_PATH" "$CLAUDE_DOWNLOAD_URL"; then echo "❌ Failed to download Claude Desktop installer from $CLAUDE_DOWNLOAD_URL" exit 1 fi echo "✓ Download complete: $CLAUDE_EXE_FILENAME" echo "📦 Extracting resources from $CLAUDE_EXE_FILENAME into separate directory..." CLAUDE_EXTRACT_DIR="$WORK_DIR/claude-extract" mkdir -p "$CLAUDE_EXTRACT_DIR" if ! 7z x -y "$CLAUDE_EXE_PATH" -o"$CLAUDE_EXTRACT_DIR"; then echo "❌ Failed to extract installer" cd "$PROJECT_ROOT" && exit 1 fi cd "$CLAUDE_EXTRACT_DIR" # Change into the extract dir to find files NUPKG_PATH_RELATIVE=$(find . -maxdepth 1 -name "AnthropicClaude-*.nupkg" | head -1) if [ -z "$NUPKG_PATH_RELATIVE" ]; then echo "❌ Could not find AnthropicClaude nupkg file in $CLAUDE_EXTRACT_DIR" cd "$PROJECT_ROOT" && exit 1 fi NUPKG_PATH="$CLAUDE_EXTRACT_DIR/$NUPKG_PATH_RELATIVE" echo "Found nupkg: $NUPKG_PATH_RELATIVE (in $CLAUDE_EXTRACT_DIR)" VERSION=$(echo "$NUPKG_PATH_RELATIVE" | LC_ALL=C grep -oP 'AnthropicClaude-\K[0-9]+\.[0-9]+\.[0-9]+(?=-full|-arm64-full)') if [ -z "$VERSION" ]; then echo "❌ Could not extract version from nupkg filename: $NUPKG_PATH_RELATIVE" cd "$PROJECT_ROOT" && exit 1 fi echo "✓ Detected Claude version: $VERSION" if ! 7z x -y "$NUPKG_PATH_RELATIVE"; then echo "❌ Failed to extract nupkg" cd "$PROJECT_ROOT" && exit 1 fi echo "✓ Resources extracted from nupkg" EXE_RELATIVE_PATH="lib/net45/claude.exe" # Check if this path is correct for arm64 too if [ ! -f "$EXE_RELATIVE_PATH" ]; then echo "❌ Cannot find claude.exe at expected path within extraction dir: $CLAUDE_EXTRACT_DIR/$EXE_RELATIVE_PATH" cd "$PROJECT_ROOT" && exit 1 fi echo "🎨 Processing icons from $EXE_RELATIVE_PATH..." if ! wrestool -x -t 14 "$EXE_RELATIVE_PATH" -o claude.ico; then echo "❌ Failed to extract icons from exe" cd "$PROJECT_ROOT" && exit 1 fi if ! icotool -x claude.ico; then echo "❌ Failed to convert icons" cd "$PROJECT_ROOT" && exit 1 fi cp claude_*.png "$WORK_DIR/" echo "✓ Icons processed and copied to $WORK_DIR" echo "⚙️ Processing app.asar..." cp "$CLAUDE_EXTRACT_DIR/lib/net45/resources/app.asar" "$APP_STAGING_DIR/" cp -a "$CLAUDE_EXTRACT_DIR/lib/net45/resources/app.asar.unpacked" "$APP_STAGING_DIR/" cd "$APP_STAGING_DIR" "$ASAR_EXEC" extract app.asar app.asar.contents echo "Creating stub native module..." cat > app.asar.contents/node_modules/claude-native/index.js << EOF // Stub implementation of claude-native using KeyboardKey enum values const KeyboardKey = { Backspace: 43, Tab: 280, Enter: 261, Shift: 272, Control: 61, Alt: 40, CapsLock: 56, Escape: 85, Space: 276, PageUp: 251, PageDown: 250, End: 83, Home: 154, LeftArrow: 175, UpArrow: 282, RightArrow: 262, DownArrow: 81, Delete: 79, Meta: 187 }; Object.freeze(KeyboardKey); module.exports = { getWindowsVersion: () => "10.0.0", setWindowEffect: () => {}, removeWindowEffect: () => {}, getIsMaximized: () => false, flashFrame: () => {}, clearFlashFrame: () => {}, showNotification: () => {}, setProgressBar: () => {}, clearProgressBar: () => {}, setOverlayIcon: () => {}, clearOverlayIcon: () => {}, KeyboardKey }; EOF mkdir -p app.asar.contents/resources mkdir -p app.asar.contents/resources/i18n cp "$CLAUDE_EXTRACT_DIR/lib/net45/resources/Tray"* app.asar.contents/resources/ cp "$CLAUDE_EXTRACT_DIR/lib/net45/resources/"*-*.json app.asar.contents/resources/i18n/ echo "##############################################################" echo "Removing "'!'" from 'if ("'!'"isWindows && isMainWindow) return null;'" echo "detection flag to to enable title bar" echo "Current working directory: '$PWD'" SEARCH_BASE="app.asar.contents/.vite/renderer/main_window/assets" TARGET_PATTERN="MainWindowPage-*.js" echo "Searching for '$TARGET_PATTERN' within '$SEARCH_BASE'..." # Find the target file recursively (ensure only one matches) TARGET_FILES=$(find "$SEARCH_BASE" -type f -name "$TARGET_PATTERN") # Count non-empty lines to get the number of files found NUM_FILES=$(echo "$TARGET_FILES" | grep -c .) if [ "$NUM_FILES" -eq 0 ]; then echo "Error: No file matching '$TARGET_PATTERN' found within '$SEARCH_BASE'." >&2 exit 1 elif [ "$NUM_FILES" -gt 1 ]; then echo "Error: Expected exactly one file matching '$TARGET_PATTERN' within '$SEARCH_BASE', but found $NUM_FILES." >&2 echo "Found files:" >&2 echo "$TARGET_FILES" >&2 exit 1 else # Exactly one file found TARGET_FILE="$TARGET_FILES" # Assign the found file path echo "Found target file: $TARGET_FILE" echo "Attempting to replace patterns like 'if(!VAR1 && VAR2)' with 'if(VAR1 && VAR2)' in $TARGET_FILE..." # Use character classes [a-zA-Z]+ to match minified variable names # Capture group 1: first variable name # Capture group 2: second variable name sed -i -E 's/if\(!([a-zA-Z]+)[[:space:]]*&&[[:space:]]*([a-zA-Z]+)\)/if(\1 \&\& \2)/g' "$TARGET_FILE" # Verification: Check if the original pattern structure still exists if ! grep -q -E 'if\(![a-zA-Z]+[[:space:]]*&&[[:space:]]*[a-zA-Z]+\)' "$TARGET_FILE"; then echo "Successfully replaced patterns like 'if(!VAR1 && VAR2)' with 'if(VAR1 && VAR2)' in $TARGET_FILE" else echo "Error: Failed to replace patterns like 'if(!VAR1 && VAR2)' in $TARGET_FILE. Check file contents." >&2 exit 1 fi fi echo "##############################################################" "$ASAR_EXEC" pack app.asar.contents app.asar mkdir -p "$APP_STAGING_DIR/app.asar.unpacked/node_modules/claude-native" cat > "$APP_STAGING_DIR/app.asar.unpacked/node_modules/claude-native/index.js" << EOF // Stub implementation of claude-native using KeyboardKey enum values const KeyboardKey = { Backspace: 43, Tab: 280, Enter: 261, Shift: 272, Control: 61, Alt: 40, CapsLock: 56, Escape: 85, Space: 276, PageUp: 251, PageDown: 250, End: 83, Home: 154, LeftArrow: 175, UpArrow: 282, RightArrow: 262, DownArrow: 81, Delete: 79, Meta: 187 }; Object.freeze(KeyboardKey); module.exports = { getWindowsVersion: () => "10.0.0", setWindowEffect: () => {}, removeWindowEffect: () => {}, getIsMaximized: () => false, flashFrame: () => {}, clearFlashFrame: () => {}, showNotification: () => {}, setProgressBar: () => {}, clearProgressBar: () => {}, setOverlayIcon: () => {}, clearOverlayIcon: () => {}, KeyboardKey }; EOF echo "Copying chosen electron installation to staging area..." mkdir -p "$APP_STAGING_DIR/node_modules/" ELECTRON_DIR_NAME=$(basename "$CHOSEN_ELECTRON_MODULE_PATH") echo "Copying from $CHOSEN_ELECTRON_MODULE_PATH to $APP_STAGING_DIR/node_modules/" cp -a "$CHOSEN_ELECTRON_MODULE_PATH" "$APP_STAGING_DIR/node_modules/" STAGED_ELECTRON_BIN="$APP_STAGING_DIR/node_modules/$ELECTRON_DIR_NAME/dist/electron" if [ -f "$STAGED_ELECTRON_BIN" ]; then echo "Setting executable permission on staged Electron binary: $STAGED_ELECTRON_BIN" chmod +x "$STAGED_ELECTRON_BIN" else echo "Warning: Staged Electron binary not found at expected path: $STAGED_ELECTRON_BIN" fi # Ensure Electron locale files are available ELECTRON_RESOURCES_SRC="$CHOSEN_ELECTRON_MODULE_PATH/dist/resources" ELECTRON_RESOURCES_DEST="$APP_STAGING_DIR/node_modules/$ELECTRON_DIR_NAME/dist/resources" if [ -d "$ELECTRON_RESOURCES_SRC" ]; then echo "Copying Electron locale resources..." mkdir -p "$ELECTRON_RESOURCES_DEST" cp -a "$ELECTRON_RESOURCES_SRC"/* "$ELECTRON_RESOURCES_DEST/" echo "✓ Electron locale resources copied" else echo "⚠️ Warning: Electron resources directory not found at $ELECTRON_RESOURCES_SRC" fi # Copy Claude locale JSON files to Electron resources directory where they're expected CLAUDE_LOCALE_SRC="$CLAUDE_EXTRACT_DIR/lib/net45/resources" echo "Copying Claude locale JSON files to Electron resources directory..." if [ -d "$CLAUDE_LOCALE_SRC" ]; then # Copy Claude's locale JSON files to the Electron resources directory cp "$CLAUDE_LOCALE_SRC/"*-*.json "$ELECTRON_RESOURCES_DEST/" echo "✓ Claude locale JSON files copied to Electron resources directory" else echo "⚠️ Warning: Claude locale source directory not found at $CLAUDE_LOCALE_SRC" fi echo "✓ app.asar processed and staged in $APP_STAGING_DIR" cd "$PROJECT_ROOT" echo -e "\033[1;36m--- Call Packaging Script ---\033[0m" FINAL_OUTPUT_PATH="" FINAL_DESKTOP_FILE_PATH="" if [ "$BUILD_FORMAT" = "deb" ]; then echo "📦 Calling Debian packaging script for $ARCHITECTURE..." chmod +x scripts/build-deb-package.sh if ! scripts/build-deb-package.sh \ "$VERSION" "$ARCHITECTURE" "$WORK_DIR" "$APP_STAGING_DIR" \ "$PACKAGE_NAME" "$MAINTAINER" "$DESCRIPTION"; then echo "❌ Debian packaging script failed." exit 1 fi DEB_FILE=$(find "$WORK_DIR" -maxdepth 1 -name "${PACKAGE_NAME}_${VERSION}_${ARCHITECTURE}.deb" | head -n 1) echo "✓ Debian Build complete!" if [ -n "$DEB_FILE" ] && [ -f "$DEB_FILE" ]; then FINAL_OUTPUT_PATH="./$(basename "$DEB_FILE")" # Set final path using basename directly mv "$DEB_FILE" "$FINAL_OUTPUT_PATH" echo "Package created at: $FINAL_OUTPUT_PATH" else echo "Warning: Could not determine final .deb file path from $WORK_DIR for ${ARCHITECTURE}." FINAL_OUTPUT_PATH="Not Found" fi elif [ "$BUILD_FORMAT" = "appimage" ]; then echo "📦 Calling AppImage packaging script for $ARCHITECTURE..." chmod +x scripts/build-appimage.sh if ! scripts/build-appimage.sh \ "$VERSION" "$ARCHITECTURE" "$WORK_DIR" "$APP_STAGING_DIR" "$PACKAGE_NAME"; then echo "❌ AppImage packaging script failed." exit 1 fi APPIMAGE_FILE=$(find "$WORK_DIR" -maxdepth 1 -name "${PACKAGE_NAME}-${VERSION}-${ARCHITECTURE}.AppImage" | head -n 1) echo "✓ AppImage Build complete!" if [ -n "$APPIMAGE_FILE" ] && [ -f "$APPIMAGE_FILE" ]; then FINAL_OUTPUT_PATH="./$(basename "$APPIMAGE_FILE")" mv "$APPIMAGE_FILE" "$FINAL_OUTPUT_PATH" echo "Package created at: $FINAL_OUTPUT_PATH" echo -e "\033[1;36m--- Generate .desktop file for AppImage ---\033[0m" FINAL_DESKTOP_FILE_PATH="./${PACKAGE_NAME}-appimage.desktop" echo "📝 Generating .desktop file for AppImage at $FINAL_DESKTOP_FILE_PATH..." cat > "$FINAL_DESKTOP_FILE_PATH" << EOF [Desktop Entry] Name=Claude (AppImage) Comment=Claude Desktop (AppImage Version $VERSION) Exec=$(basename "$FINAL_OUTPUT_PATH") %u Icon=claude-desktop Type=Application Terminal=false Categories=Office;Utility;Network; MimeType=x-scheme-handler/claude; StartupWMClass=Claude X-AppImage-Version=$VERSION X-AppImage-Name=Claude Desktop (AppImage) EOF echo "✓ .desktop file generated." else echo "Warning: Could not determine final .AppImage file path from $WORK_DIR for ${ARCHITECTURE}." FINAL_OUTPUT_PATH="Not Found" fi fi echo -e "\033[1;36m--- Cleanup ---\033[0m" if [ "$PERFORM_CLEANUP" = true ]; then echo "🧹 Cleaning up intermediate build files in $WORK_DIR..." if rm -rf "$WORK_DIR"; then echo "✓ Cleanup complete ($WORK_DIR removed)." else echo "⚠️ Cleanup command (rm -rf $WORK_DIR) failed." fi else echo "Skipping cleanup of intermediate build files in $WORK_DIR." fi echo "✅ Build process finished." echo -e "\n\033[1;34m====== Next Steps ======\033[0m" if [ "$BUILD_FORMAT" = "deb" ]; then if [ "$FINAL_OUTPUT_PATH" != "Not Found" ] && [ -e "$FINAL_OUTPUT_PATH" ]; then echo -e "📦 To install the Debian package, run:" echo -e " \033[1;32msudo apt install $FINAL_OUTPUT_PATH\033[0m" echo -e " (or \`sudo dpkg -i $FINAL_OUTPUT_PATH\`)" else echo -e "⚠️ Debian package file not found. Cannot provide installation instructions." fi elif [ "$BUILD_FORMAT" = "appimage" ]; then if [ "$FINAL_OUTPUT_PATH" != "Not Found" ] && [ -e "$FINAL_OUTPUT_PATH" ]; then echo -e "✅ AppImage created at: \033[1;36m$FINAL_OUTPUT_PATH\033[0m" echo -e "\n\033[1;33mIMPORTANT:\033[0m This AppImage requires \033[1;36mGear Lever\033[0m for proper desktop integration" echo -e "and to handle the \`claude://\` login process correctly." echo -e "\n🚀 To install Gear Lever:" echo -e " 1. Install via Flatpak:" echo -e " \033[1;32mflatpak install flathub it.mijorus.gearlever\033[0m" echo -e " - or visit: \033[1;34mhttps://flathub.org/apps/it.mijorus.gearlever\033[0m" echo -e " 2. Integrate your AppImage with just one click:" echo -e " - Open Gear Lever" echo -e " - Drag and drop \033[1;36m$FINAL_OUTPUT_PATH\033[0m into Gear Lever" echo -e " - Click 'Integrate' to add it to your app menu" if [ "$GITHUB_ACTIONS" = "true" ]; then echo -e "\n \033[1;32m✓\033[0m This AppImage includes embedded update information!" echo -e " \033[1;32m✓\033[0m Gear Lever will automatically detect and handle updates from GitHub releases." echo -e " \033[1;32m✓\033[0m No manual update URL configuration needed." else echo -e "\n \033[1;33mℹ\033[0m This locally-built AppImage does not include update information." echo -e " \033[1;33mℹ\033[0m You can manually configure updates in Gear Lever:" echo -e " 3. Configure manual updates (optional):" echo -e " - In Gear Lever, select your integrated Claude Desktop" echo -e " - Choose 'Github' as update source" echo -e " - Use this update URL: \033[1;33mhttps://github.com/aaddrick/claude-desktop-debian/releases/download/*/claude-desktop-*-${ARCHITECTURE}.AppImage\033[0m" echo -e " \033[1;34m→\033[0m For automatic updates, download release versions: https://github.com/aaddrick/claude-desktop-debian/releases" fi else echo -e "⚠️ AppImage file not found. Cannot provide usage instructions." fi fi echo -e "\033[1;34m======================\033[0m" exit 0