Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
"build:rust:beta": "cargo build --release --features beta",
"build:app": "./scripts/macos/build-macos-app.sh prod",
"build:app:beta": "./scripts/macos/build-macos-app.sh beta",
"build:archive": "./scripts/macos/create-archive.sh $(cat .version)",
"build:dmg": "./scripts/macos/create-dmg.sh $(cat .version)",
"build:universal": "./scripts/macos/build-universal.sh",
"build:installer": "bun run build:app && ./scripts/macos/create-dmg.sh $(cat .version)",
"build:installer:beta": "bun run build:app:beta && ./scripts/macos/create-dmg.sh $(cat .version)",
"build:installer": "bun run build:app && bun run build:archive && bun run build:dmg",
"build:installer:beta": "bun run build:app:beta && bun run build:archive && bun run build:dmg",
"audit": "bun audit",
"prepare": "git config --unset-all --local core.hooksPath || true && prek install || true",
"bump": "bash scripts/bump-version.sh"
Expand Down
86 changes: 61 additions & 25 deletions scripts/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,57 +26,72 @@ if [[ "$OSTYPE" != "darwin"* ]]; then
fi

# Get the latest release version
echo "📥 Fetching latest release..."
LATEST_RELEASE=$(curl -s https://api.github.com/repos/$REPO/releases/latest | grep '"tag_name"' | sed -E 's/.*"v([^"]+)".*/\1/')
echo "📥 Fetching latest release information..."
# Use a more robust way to get the latest tag
LATEST_RELEASE=$(curl -sL https://api.github.com/repos/$REPO/releases/latest | grep '"tag_name"' | sed -E 's/.*"v?([^"]+)".*/\1/')

if [ -z "$LATEST_RELEASE" ]; then
echo -e "${RED}❌ Error: Could not fetch latest release${NC}"
echo -e "${RED}❌ Error: Could not fetch latest release version from GitHub API${NC}"
exit 1
fi

echo " Latest version: $LATEST_RELEASE"

# Try to download tar.gz archive (preferred for script-based install)
ARCHIVE_URL="https://github.com/$REPO/releases/download/v$LATEST_RELEASE/chase-ai-$LATEST_RELEASE-macos.tar.gz"
ARCHIVE_FILE="/tmp/chase-ai-$LATEST_RELEASE.tar.gz"
ARCHIVE_FILE="/tmp/chase-ai-$LATEST_RELEASE-macos.tar.gz"

echo "📦 Downloading ChaseAI $LATEST_RELEASE..."
if ! curl -sL -f -o "$ARCHIVE_FILE" "$ARCHIVE_URL"; then
DOWNLOAD_SUCCESS=false

# Use -f to fail on HTTP errors
if curl -sL -f -o "$ARCHIVE_FILE" "$ARCHIVE_URL"; then
DOWNLOAD_SUCCESS=true
IS_DMG=false
else
echo -e "${YELLOW}⚠ Warning: Failed to download tar.gz, trying DMG fallback...${NC}"
DMG_URL="https://github.com/$REPO/releases/download/v$LATEST_RELEASE/chase-ai-$LATEST_RELEASE-macos.dmg"
ARCHIVE_FILE="/tmp/chase-ai-$LATEST_RELEASE.dmg"
if ! curl -sL -f -o "$ARCHIVE_FILE" "$DMG_URL"; then
echo -e "${RED}❌ Error: Failed to download both tar.gz and DMG${NC}"
exit 1
ARCHIVE_FILE="/tmp/chase-ai-$LATEST_RELEASE-macos.dmg"
if curl -sL -f -o "$ARCHIVE_FILE" "$DMG_URL"; then
DOWNLOAD_SUCCESS=true
IS_DMG=true
else
# Try without 'v' prefix in download URL if it failed
ARCHIVE_URL_NO_V="https://github.com/$REPO/releases/download/$LATEST_RELEASE/chase-ai-$LATEST_RELEASE-macos.tar.gz"
ARCHIVE_FILE="/tmp/chase-ai-$LATEST_RELEASE-macos.tar.gz"
if curl -sL -f -o "$ARCHIVE_FILE" "$ARCHIVE_URL_NO_V"; then
DOWNLOAD_SUCCESS=true
IS_DMG=false
fi
fi
IS_DMG=true
else
IS_DMG=false
fi

if [ "$DOWNLOAD_SUCCESS" = false ]; then
echo -e "${RED}❌ Error: Failed to download both tar.gz and DMG for version $LATEST_RELEASE${NC}"
echo " Check if the assets exist at: https://github.com/$REPO/releases/latest"
exit 1
fi

# Verify file is not empty and not a 404 page
FILE_SIZE=$(stat -f%z "$ARCHIVE_FILE" 2>/dev/null || stat -c%s "$ARCHIVE_FILE" 2>/dev/null || echo "0")
if [ "$FILE_SIZE" -lt 1000 ]; then
echo -e "${RED}❌ Error: Downloaded file is too small ($FILE_SIZE bytes). It might be a 404 page.${NC}"
if [ "$FILE_SIZE" -lt 10000 ]; then
echo -e "${RED}❌ Error: Downloaded file is too small ($FILE_SIZE bytes). Download might be corrupted or asset is missing.${NC}"
rm -f "$ARCHIVE_FILE"
exit 1
fi

# Verify checksum if available
CHECKSUMS_URL="https://github.com/$REPO/releases/download/v$LATEST_RELEASE/checksums.sha256"
CHECKSUMS_FILE="/tmp/checksums.sha256"
if curl -s -f "$CHECKSUMS_URL" > "$CHECKSUMS_FILE" 2>/dev/null && [ -s "$CHECKSUMS_FILE" ]; then
if curl -sL -f -o "$CHECKSUMS_FILE" "$CHECKSUMS_URL" 2>/dev/null || curl -sL -f -o "$CHECKSUMS_FILE" "https://github.com/$REPO/releases/download/$LATEST_RELEASE/checksums.sha256" 2>/dev/null; then
echo "🔐 Verifying checksum..."

FILENAME=$(basename "$ARCHIVE_FILE")
EXPECTED_CHECKSUM=$(grep "$FILENAME" "$CHECKSUMS_FILE" | awk '{print $1}')

if [ -z "$EXPECTED_CHECKSUM" ]; then
echo -e "${YELLOW}⚠ Warning: Could not find checksum for $FILENAME in checksums.sha256${NC}"
else
# Calculate actual checksum
ACTUAL_CHECKSUM=$(shasum -a 256 "$ARCHIVE_FILE" | awk '{print $1}')

if [ "$EXPECTED_CHECKSUM" = "$ACTUAL_CHECKSUM" ]; then
echo -e "${GREEN}✓ Checksum verified${NC}"
else
Expand All @@ -88,7 +103,7 @@ if curl -s -f "$CHECKSUMS_URL" > "$CHECKSUMS_FILE" 2>/dev/null && [ -s "$CHECKSU
fi
fi
else
echo -e "${YELLOW}⚠ Warning: Could not download checksums.sha256 file, skipping verification${NC}"
echo -e "${YELLOW}⚠ Warning: Could not download checksums.sha256, skipping verification${NC}"
fi

# Extract and install
Expand All @@ -97,34 +112,55 @@ MOUNT_POINT=$(mktemp -d)

if [ "$IS_DMG" = true ]; then
echo " Mounting DMG..."
hdiutil attach "$ARCHIVE_FILE" -mountpoint "$MOUNT_POINT" -nobrowse
if ! hdiutil attach "$ARCHIVE_FILE" -mountpoint "$MOUNT_POINT" -nobrowse -quiet; then
echo -e "${RED}❌ Error: Failed to mount DMG. The file might be corrupted.${NC}"
rm -f "$ARCHIVE_FILE"
exit 1
fi
SOURCE_PATH="$MOUNT_POINT/$APP_NAME"
else
echo " Extracting archive..."
tar -xzf "$ARCHIVE_FILE" -C "$MOUNT_POINT"
SOURCE_PATH="$MOUNT_POINT/$APP_NAME"
if ! tar -xzf "$ARCHIVE_FILE" -C "$MOUNT_POINT"; then
echo -e "${RED}❌ Error: Failed to extract tar.gz archive. The file might be corrupted.${NC}"
rm -f "$ARCHIVE_FILE"
exit 1
fi
# Sometimes the app is inside a subfolder in the archive
SOURCE_PATH=$(find "$MOUNT_POINT" -name "$APP_NAME" -maxdepth 2 | head -n 1)
fi

# Copy app to Applications
if [ -z "$SOURCE_PATH" ] || [ ! -d "$SOURCE_PATH" ]; then
echo -e "${RED}❌ Error: Could not find $APP_NAME in the downloaded package${NC}"
# Cleanup
if [ "$IS_DMG" = true ]; then hdiutil detach "$MOUNT_POINT" -quiet || true; fi
rm -f "$ARCHIVE_FILE"
rm -rf "$MOUNT_POINT"
exit 1
fi

echo "📋 Copying to $INSTALL_DIR..."
if [ -d "$INSTALL_DIR/$APP_NAME" ]; then
echo " Removing existing installation..."
rm -rf "$INSTALL_DIR/$APP_NAME"
fi

cp -r "$SOURCE_PATH" "$INSTALL_DIR/"
cp -R "$SOURCE_PATH" "$INSTALL_DIR/"

# Cleanup
if [ "$IS_DMG" = true ]; then
echo " Unmounting DMG..."
hdiutil detach "$MOUNT_POINT"
hdiutil detach "$MOUNT_POINT" -quiet
fi

rm -f "$ARCHIVE_FILE"
rm -rf "$MOUNT_POINT"

# Verify installation
if [ -d "$INSTALL_DIR/$APP_NAME" ]; then
# Remove quarantine attributes to allow the app to run
xattr -dr com.apple.quarantine "$INSTALL_DIR/$APP_NAME" 2>/dev/null || true

echo -e "${GREEN}✅ Installation successful!${NC}"
echo ""
echo "📍 ChaseAI installed to: $INSTALL_DIR/$APP_NAME"
Expand All @@ -134,6 +170,6 @@ if [ -d "$INSTALL_DIR/$APP_NAME" ]; then
echo ""
echo "💡 Or use Spotlight search (Cmd+Space) and type 'ChaseAI'"
else
echo -e "${RED}❌ Error: Installation failed${NC}"
echo -e "${RED}❌ Error: Installation failed - $APP_NAME not found in $INSTALL_DIR${NC}"
exit 1
fi
26 changes: 12 additions & 14 deletions scripts/macos/create-archive.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,19 @@

# Create tar.gz archive for macOS
# This script packages the ChaseAI application into a tar.gz archive
# as a fallback to DMG creation

set -e

# Navigate to the project root
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd "$SCRIPT_DIR/../.."

echo "📦 Creating tar.gz archive..."
echo "📦 Creating macOS tar.gz archive..."

# Configuration
APP_NAME="ChaseAI"
BINARY_NAME="chase-ai"
VERSION="${1:-0.1.0}"
VERSION="${1:-$(grep '^version =' Cargo.toml | head -1 | cut -d '"' -f2)}"
RELEASE_DIR="target/release"
APP_BUNDLE="${RELEASE_DIR}/${APP_NAME}.app"
ARCHIVE_NAME="${BINARY_NAME}-${VERSION}-macos.tar.gz"
Expand All @@ -28,13 +27,15 @@ echo " Archive Path: ${ARCHIVE_PATH}"
# Verify app bundle exists
if [ ! -d "${APP_BUNDLE}" ]; then
echo "❌ Error: App bundle not found at ${APP_BUNDLE}"
echo " Please run scripts/macos/build-macos-app.sh first"
exit 1
fi

echo "✓ App bundle found"

# Create tar.gz archive
echo "🎨 Creating tar.gz archive..."
rm -f "${ARCHIVE_PATH}"
cd "${RELEASE_DIR}"
tar -czf "${ARCHIVE_NAME}" "${APP_NAME}.app"
cd - > /dev/null
Expand All @@ -45,30 +46,27 @@ if [ ! -f "${ARCHIVE_PATH}" ]; then
exit 1
fi

echo "✓ Archive file created"

# Check archive file size
ARCHIVE_SIZE=$(stat -f%z "${ARCHIVE_PATH}" 2>/dev/null || stat -c%s "${ARCHIVE_PATH}" 2>/dev/null || echo "unknown")
echo " Size: ${ARCHIVE_SIZE} bytes"
echo "✓ Archive created successfully"

# Generate checksums
echo "🔐 Generating checksums..."
SHA256_FILE="${ARCHIVE_PATH}.sha256"
shasum -a 256 "${ARCHIVE_PATH}" > "${SHA256_FILE}"

# Display checksum
echo " SHA256: $(cat ${SHA256_FILE})"
echo " SHA256: $(cat ${SHA256_FILE} | awk '{print $1}')"

# Display file info
ARCHIVE_SIZE=$(du -h "${ARCHIVE_PATH}" | cut -f1)
if [[ "$OSTYPE" == "darwin"* ]]; then
ARCHIVE_SIZE=$(du -h "${ARCHIVE_PATH}" | cut -f1)
else
ARCHIVE_SIZE=$(ls -lh "${ARCHIVE_PATH}" | awk '{print $5}')
fi

echo ""
echo "✅ Archive created successfully!"
echo " Name: ${ARCHIVE_NAME}"
echo " Size: ${ARCHIVE_SIZE}"
echo " Path: ${ARCHIVE_PATH}"
echo " Checksum: ${SHA256_FILE}"
echo ""
echo "To extract the archive:"
echo " tar -xzf ${ARCHIVE_NAME}"
echo " cp -r ${APP_NAME}.app /Applications/"
echo ""
66 changes: 30 additions & 36 deletions scripts/macos/create-dmg.sh
Original file line number Diff line number Diff line change
@@ -1,79 +1,73 @@
#!/bin/bash

# Create installer for macOS
# Creates a tar.gz archive of the ChaseAI application
# Create DMG installer for macOS
# This script packages the ChaseAI application into a DMG file

set -e

# Navigate to the project root
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd "$SCRIPT_DIR/../.."

echo "📦 Creating macOS installer..."
echo "📦 Creating macOS DMG installer..."

# Configuration
APP_NAME="ChaseAI"
BINARY_NAME="chase-ai"
VERSION="${1:-0.1.0}"
VERSION="${1:-$(grep '^version =' Cargo.toml | head -1 | cut -d '"' -f2)}"
RELEASE_DIR="target/release"
APP_BUNDLE="${RELEASE_DIR}/${APP_NAME}.app"
ARCHIVE_NAME="${BINARY_NAME}-${VERSION}-macos.tar.gz"
ARCHIVE_PATH="${RELEASE_DIR}/${ARCHIVE_NAME}"
DMG_NAME="${BINARY_NAME}-${VERSION}-macos.dmg"
DMG_PATH="${RELEASE_DIR}/${DMG_NAME}"

echo " Version: ${VERSION}"
echo " App Bundle: ${APP_BUNDLE}"
echo " DMG Path: ${DMG_PATH}"

# Verify app bundle exists
if [ ! -d "${APP_BUNDLE}" ]; then
echo "❌ Error: App bundle not found at ${APP_BUNDLE}"
echo " Please run scripts/macos/build-macos-app.sh first"
ls -la "${RELEASE_DIR}/" || echo "Release directory doesn't exist"
exit 1
fi

echo "✓ App bundle found"

# Create tar.gz archive
echo "🎨 Creating tar.gz archive..."
cd "${RELEASE_DIR}"
tar -czf "${ARCHIVE_NAME}" "${APP_NAME}.app"
cd - > /dev/null

# Verify archive was created
if [ ! -f "${ARCHIVE_PATH}" ]; then
echo "❌ Error: Archive creation failed"
exit 1
# Check if running on macOS
if [[ "$OSTYPE" != "darwin"* ]]; then
echo "⚠️ Warning: hdiutil is only available on macOS. Skipping DMG creation on this platform."
# On non-macOS, we don't want to fail the build if it's just a check,
# but for a real release it must be run on macOS.
exit 0
fi

echo "✓ Archive created successfully"

# Check archive file size
ARCHIVE_SIZE=$(stat -f%z "${ARCHIVE_PATH}" 2>/dev/null || stat -c%s "${ARCHIVE_PATH}" 2>/dev/null || echo "0")
echo " Size: ${ARCHIVE_SIZE} bytes"
# Create DMG
echo "🎨 Creating DMG..."
rm -f "${DMG_PATH}"
hdiutil create -volname "${APP_NAME}" -srcfolder "${APP_BUNDLE}" -ov -format UDZO "${DMG_PATH}"

if [ "${ARCHIVE_SIZE}" -lt 1000000 ]; then
echo "⚠️ Warning: Archive file is very small (${ARCHIVE_SIZE} bytes)"
# Verify DMG was created
if [ ! -f "${DMG_PATH}" ]; then
echo "❌ Error: DMG creation failed"
exit 1
fi

echo "✓ DMG created successfully"

# Generate checksums
echo "🔐 Generating checksums..."
SHA256_FILE="${ARCHIVE_PATH}.sha256"
shasum -a 256 "${ARCHIVE_PATH}" > "${SHA256_FILE}"
SHA256_FILE="${DMG_PATH}.sha256"
shasum -a 256 "${DMG_PATH}" > "${SHA256_FILE}"

# Display checksum
echo " SHA256: $(cat ${SHA256_FILE})"
echo " SHA256: $(cat ${SHA256_FILE} | awk '{print $1}')"

# Display file info
ARCHIVE_SIZE=$(du -h "${ARCHIVE_PATH}" | cut -f1)
DMG_SIZE=$(du -h "${DMG_PATH}" | cut -f1)
echo ""
echo "✅ Installer created successfully!"
echo " Name: ${ARCHIVE_NAME}"
echo " Size: ${ARCHIVE_SIZE}"
echo " Path: ${ARCHIVE_PATH}"
echo "✅ DMG created successfully!"
echo " Name: ${DMG_NAME}"
echo " Size: ${DMG_SIZE}"
echo " Path: ${DMG_PATH}"
echo " Checksum: ${SHA256_FILE}"
echo ""
echo "To extract and install:"
echo " tar -xzf ${ARCHIVE_NAME}"
echo " cp -r ${APP_NAME}.app /Applications/"
echo ""
Loading