Files
home/Apps/claude-desktop-debian/build.sh
2025-10-08 10:35:48 +02:00

623 lines
26 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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