Skip to content

Commit

Permalink
Lots of work to build app
Browse files Browse the repository at this point in the history
  • Loading branch information
dougollerenshaw committed Oct 15, 2024
1 parent 3a9bd83 commit 1bcae43
Show file tree
Hide file tree
Showing 8 changed files with 244 additions and 146 deletions.
31 changes: 23 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ https://github.com/user-attachments/assets/8aa729ff-c431-4a61-a9ef-d17050a27d02
### Prerequisites

- Python 3.9 or higher
- Conda (Miniconda or Anaconda)

### Setup

Expand All @@ -47,14 +48,28 @@ https://github.com/user-attachments/assets/8aa729ff-c431-4a61-a9ef-d17050a27d02
pip install -r requirements.txt
```

4. Set up your Anthropic API key:
- Set up a developer account with Anthropic and get an API key at https://console.anthropic.com/dashboard
- You'll need to pre-fund your account to cover API costs. Current costs (as of Sept 15, 2024) are $0.003 and $0.015 per 1000 tokens for input and output, respectively. Long conversations will obviously cost more. Fund your account with something small (maybe $5) to start with, then add more if you find this tool useful.
- Create a `.env` file in the project root
- Add your API key to the file:
```
ANTHROPIC_API_KEY="your_api_key_here" # make sure the key is in quotes
```
Note: These instructions should work for most systems (macOS, including Apple Silicon, Windows, and Linux). If you encounter any architecture-specific issues, please refer to the troubleshooting section below or open an issue on GitHub.

### Troubleshooting

If you experience architecture-specific issues (e.g., on Apple Silicon Macs), try the following:

1. Ensure Conda is using the correct architecture:
```
conda info
```
Look for the "platform" field to confirm it matches your system architecture.

2. If needed, you can force Conda to use a specific architecture:
```
CONDA_SUBDIR=osx-arm64 conda create -n codeaide python=3.11 # For Apple Silicon
conda activate codeaide
conda config --env --set subdir osx-arm64 # For Apple Silicon
```

3. Then proceed with step 3 of the regular installation process.

For other architecture-specific issues, please open an issue on GitHub with details about your system and the problem you're encountering.

## Usage

Expand Down
91 changes: 12 additions & 79 deletions build_scripts/build_codeaide.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,7 @@
# Exit on any error
set -e

# Function to check if a command exists
command_exists () {
type "$1" &> /dev/null ;
}

# Check prerequisites
if ! command_exists conda ; then
echo "conda is not installed. Please install it and try again."
exit 1
fi

if ! command_exists brew ; then
echo "Homebrew is not installed. Please install it and try again."
exit 1
fi

# Check if notarization is requested
NOTARIZE=false
if [ "$1" = "--notarize" ]; then
NOTARIZE=true
fi

# Activate conda environment
# Activate the correct conda environment
eval "$(conda shell.bash hook)"
conda activate codeaide

Expand All @@ -43,50 +21,32 @@ mkdir -p ~/Library/Logs/CodeAide
echo "Ensuring application data directory exists..."
mkdir -p ~/Library/Application\ Support/CodeAide

# Step 1: Install Required Python Packages and Download Whisper Model
# Install required packages
echo "Installing required Python packages..."
$PYTHON_PATH -m pip install --upgrade pip
$PYTHON_PATH -m pip install PyQt5 pyinstaller whisper
$PYTHON_PATH -m pip install -r requirements.txt

echo "Downloading Whisper model..."
$PYTHON_PATH <<EOF
import whisper
whisper.load_model('tiny', download_root='./codeaide/models/whisper')
EOF
# Check if Whisper is installed
echo "Checking Whisper installation..."
$PYTHON_PATH -c "import whisper; print('Whisper version:', whisper.__version__)"

# Step 2: Package the Application
# Package the application
echo "Packaging the application..."
$PYTHON_PATH -m PyInstaller build_scripts/codeaide.spec
yes | $PYTHON_PATH -m PyInstaller build_scripts/codeaide.spec

# Step 3: Create the DMG
# Create the DMG
echo "Creating DMG..."
APP_NAME="CodeAide"
DMG_NAME="${APP_NAME}.dmg"
SOURCE_DIR="dist/${APP_NAME}.app"
TEMP_DMG="temp_${DMG_NAME}"
FINAL_DMG="${DMG_NAME}"

# Clean up function
cleanup() {
echo "Cleaning up..."
for disk in $(diskutil list | grep ${APP_NAME} | awk '{print $NF}'); do
echo "Attempting to unmount and eject $disk"
diskutil unmountDisk force $disk 2>/dev/null || true
diskutil eject force $disk 2>/dev/null || true
done
rm -f "${TEMP_DMG}"
}

# Run cleanup before starting
cleanup

# Create a temporary directory for DMG contents
TEMP_DIR=$(mktemp -d)
cp -R "${SOURCE_DIR}" "${TEMP_DIR}"
ln -s /Applications "${TEMP_DIR}"

# Create the DMG directly
echo "Creating DMG..."
# Create the DMG
hdiutil create -volname "${APP_NAME}" -srcfolder "${TEMP_DIR}" -ov -format UDZO "${FINAL_DMG}"

# Clean up the temporary directory
Expand All @@ -100,32 +60,5 @@ else
exit 1
fi

if [ "$NOTARIZE" = true ]; then
# Prompt for Apple Developer information
read -p "Enter your Developer ID Application certificate name (e.g., 'Developer ID Application: Your Name'): " DEVELOPER_NAME
read -p "Enter your Apple Developer Team ID: " TEAM_ID
read -p "Enter your Apple ID: " APPLE_ID
read -s -p "Enter your app-specific password: " APP_PASSWORD
echo

# Step 4: Code Sign the Application
echo "Code signing the application..."
codesign --force --options runtime --entitlements build_scripts/entitlements.plist --sign "$DEVELOPER_NAME" "dist/CodeAide.app"

# Step 5: Notarize the DMG
echo "Notarizing the DMG..."
xcrun notarytool submit "${FINAL_DMG}" --wait --apple-id "$APPLE_ID" --password "$APP_PASSWORD" --team-id "$TEAM_ID"

# Step 6: Staple the notarization ticket to the DMG
echo "Stapling the notarization ticket..."
xcrun stapler staple "${FINAL_DMG}"

echo "Build process completed successfully!"
echo "The signed and notarized DMG is ready for distribution."
else
echo "Build process completed successfully!"
echo "The DMG is ready for testing. (Not signed or notarized)"
fi

# Final cleanup
cleanup
echo "Build process completed successfully!"
echo "The DMG is ready for testing. (Not signed or notarized)"
90 changes: 67 additions & 23 deletions build_scripts/codeaide.spec
Original file line number Diff line number Diff line change
@@ -1,19 +1,64 @@
# -*- mode: python ; coding: utf-8 -*-

import os
import sys
from PyInstaller.utils.hooks import collect_data_files

# Add the path to your conda environment's site-packages
sys.path.append('/Users/dollerenshaw/opt/anaconda3/envs/codeaide_arm64_new/lib/python3.11/site-packages')

import whisper

block_cipher = None

whisper_path = '/Users/dollerenshaw/opt/anaconda3/envs/codeaide/lib/python3.11/site-packages/whisper'
whisper_path = os.path.dirname(whisper.__file__)
whisper_assets = os.path.join(whisper_path, 'assets')

# Determine the path to your project root
project_root = os.path.abspath(os.path.join(SPECPATH, '..'))

# Collect data files
datas = [
(os.path.join(project_root, 'codeaide', 'examples.yaml'), 'codeaide'),
(os.path.join(project_root, 'codeaide', 'assets'), 'codeaide/assets'),
(whisper_assets, 'whisper/assets'),
]

# Add assets directory
assets_dir = os.path.join(project_root, 'codeaide', 'assets')
for root, dirs, files in os.walk(assets_dir):
for file in files:
file_path = os.path.join(root, file)
relative_path = os.path.relpath(root, project_root)
datas.append((file_path, relative_path))

a = Analysis(
['../codeaide.py'],
['../codeaide/__main__.py'],
pathex=[],
binaries=[],
datas=[('../codeaide/examples.yaml', 'codeaide'),
('../codeaide/assets/*', 'codeaide/assets'),
('../models', 'models'),
(os.path.join(whisper_path, 'assets'), 'whisper/assets')],
hiddenimports=['whisper'],
datas=datas + collect_data_files('whisper'),
hiddenimports=[
'PyQt5.QtCore',
'PyQt5.QtGui',
'PyQt5.QtWidgets',
'anthropic',
'google.generativeai',
'decouple',
'numpy',
'keyring',
'openai',
'hjson',
'yaml',
'pygments',
'sounddevice',
'scipy',
'openai-whisper',
'whisper',
'whisper.tokenizer',
'whisper.audio',
'whisper.model',
'whisper.transcribe',
],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
Expand All @@ -22,42 +67,41 @@ a = Analysis(
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
optimize=0,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)

exe = EXE(
pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
exclude_binaries=True,
name='CodeAide',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=False,
disable_windowed_traceback=False,
argv_emulation=True,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)

info_plist = {
'NSHighResolutionCapable': True,
'NSRequiresAquaSystemAppearance': False, # For dark mode support
}
coll = COLLECT(
exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='CodeAide',
)

app = BUNDLE(
exe,
coll,
name='CodeAide.app',
bundle_identifier='com.codeaide',
info_plist={
'NSMicrophoneUsageDescription': 'CodeAide needs access to your microphone for speech-to-text functionality.',
},
entitlements_file='build_scripts/entitlements.plist',
icon=None,
bundle_identifier=None,
)
35 changes: 25 additions & 10 deletions codeaide/__main__.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
import sys
from PyQt5.QtWidgets import QApplication, QMessageBox
from PyQt5.QtCore import QTimer, Qt
from PyQt5.QtCore import QTimer, Qt, QSharedMemory
from codeaide.logic.chat_handler import ChatHandler
from codeaide.utils import api_utils
from codeaide.ui.splash_screen import SplashScreen
from codeaide.ui.chat_window import ChatWindow
import traceback

if hasattr(Qt, "AA_EnableHighDpiScaling"):
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True)
if hasattr(Qt, "AA_UseHighDpiPixmaps"):
QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True)


def exception_hook(exctype, value, tb):
error_msg = "".join(traceback.format_exception(exctype, value, tb))
Expand All @@ -27,9 +22,31 @@ def exception_hook(exctype, value, tb):


def main():
# Create QApplication instance
app = QApplication(sys.argv)

# Check for existing instance
shared_memory = QSharedMemory("CodeAideUniqueKey")
if shared_memory.attach():
print("Application is already running.")
sys.exit(1)

if not shared_memory.create(1):
print("Failed to create shared memory.")
sys.exit(1)

sys.excepthook = exception_hook

if hasattr(Qt, "AA_EnableHighDpiScaling"):
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True)
if hasattr(Qt, "AA_UseHighDpiPixmaps"):
QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True)

# Show the splash screen immediately
splash = SplashScreen()
splash.show()
app.processEvents() # This ensures the splash screen is displayed immediately

if len(sys.argv) > 1 and sys.argv[1] == "test":
success, message = api_utils.check_api_connection()
if success:
Expand All @@ -38,14 +55,12 @@ def main():
else:
print("Connection failed.")
print("Error:", message)
sys.exit(0) # Exit after test
else:
# Show the splash screen
splash = SplashScreen()
splash.show()

# Function to update progress
def update_progress(value, message):
splash.update_progress(value, message)
app.processEvents() # Ensure UI updates

# Create ChatHandler (this might take some time)
update_progress(10, "Initializing ChatHandler...")
Expand Down
Loading

0 comments on commit 1bcae43

Please sign in to comment.