webdrop-bridge/build/scripts/build_macos.sh

320 lines
7.4 KiB
Bash
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
# Build macOS DMG package using PyInstaller
#
# This script builds the WebDrop Bridge application for macOS and creates
# a distributable DMG image.
#
# Requirements:
# - PyInstaller 6.0+
# - Python 3.10+
# - Xcode Command Line Tools (for code signing)
# - create-dmg (optional, for custom DMG: brew install create-dmg)
#
# Usage:
# bash build_macos.sh [--sign] [--notarize] [--env-file PATH]
#
# Options:
# --sign Sign app (requires Apple developer certificate)
# --notarize Notarize app (requires Apple ID)
# --env-file PATH Use custom .env file (default: project root .env)
# Build fails if .env doesn't exist
set -e # Exit on error
# Configuration
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
BUILD_DIR="$PROJECT_ROOT/build"
DIST_DIR="$BUILD_DIR/dist/macos"
TEMP_BUILD="$BUILD_DIR/temp/macos"
SPECS_DIR="$BUILD_DIR/specs"
SPEC_FILE="$BUILD_DIR/webdrop_bridge.spec"
APP_NAME="WebDropBridge"
DMG_VOLUME_NAME="WebDrop Bridge"
VERSION="1.0.0"
# Default .env file
ENV_FILE="$PROJECT_ROOT/.env"
# Parse arguments
SIGN_APP=0
NOTARIZE_APP=0
while [[ $# -gt 0 ]]; do
case $1 in
--sign)
SIGN_APP=1
shift
;;
--notarize)
NOTARIZE_APP=1
shift
;;
--env-file)
ENV_FILE="$2"
shift 2
;;
*)
echo "Unknown option: $1"
exit 1
;;
esac
done
# Validate env file
if [ ! -f "$ENV_FILE" ]; then
echo "❌ Configuration file not found: $ENV_FILE"
echo "Please provide a valid .env file or use --env-file parameter"
exit 1
fi
echo "📋 Using configuration: $ENV_FILE"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Logging functions
log_info() {
echo -e "${BLUE}${NC} $1"
}
log_success() {
echo -e "${GREEN}${NC} $1"
}
log_warning() {
echo -e "${YELLOW}⚠️${NC} $1"
}
log_error() {
echo -e "${RED}${NC} $1"
}
# Main build function
main() {
echo "=========================================="
echo "🚀 WebDrop Bridge macOS Build"
echo "=========================================="
echo ""
# Check prerequisites
check_prerequisites
# Clean previous builds
clean_builds
# Build with PyInstaller
build_executable
# Create DMG
create_dmg
# Optional: Sign and notarize
if [ $SIGN_APP -eq 1 ]; then
sign_app
fi
if [ $NOTARIZE_APP -eq 1 ]; then
notarize_app
fi
echo ""
echo "=========================================="
log_success "Build completed successfully"
echo "=========================================="
echo ""
log_info "Output: $DIST_DIR/"
ls -lh "$DIST_DIR"
}
check_prerequisites() {
log_info "Checking prerequisites..."
# Check Python
if ! command -v python3 &> /dev/null; then
log_error "Python 3 not found"
exit 1
fi
log_success "Python 3 found: $(python3 --version)"
# Check PyInstaller
if ! python3 -m pip show pyinstaller &> /dev/null; then
log_error "PyInstaller not installed. Run: pip install pyinstaller"
exit 1
fi
log_success "PyInstaller installed"
# Check spec file
if [ ! -f "$SPEC_FILE" ]; then
log_error "Spec file not found: $SPEC_FILE"
exit 1
fi
log_success "Spec file found"
echo ""
}
clean_builds() {
log_info "Cleaning previous builds..."
for dir in "$DIST_DIR" "$TEMP_BUILD" "$SPECS_DIR"; do
if [ -d "$dir" ]; then
rm -rf "$dir"
log_success "Removed $dir"
fi
done
mkdir -p "$DIST_DIR" "$TEMP_BUILD" "$SPECS_DIR"
echo ""
}
build_executable() {
log_info "Building macOS executable with PyInstaller..."
echo ""
# Export env file for spec file to pick up
export WEBDROP_ENV_FILE="$ENV_FILE"
python3 -m PyInstaller \
--distpath="$DIST_DIR" \
--buildpath="$TEMP_BUILD" \
--specpath="$SPECS_DIR" \
"$SPEC_FILE"
if [ ! -d "$DIST_DIR/$APP_NAME.app" ]; then
log_error "Application bundle not created"
exit 1
fi
log_success "Application bundle built successfully"
log_info "Output: $DIST_DIR/$APP_NAME.app"
echo ""
}
create_dmg() {
log_info "Creating DMG package..."
echo ""
DMG_FILE="$DIST_DIR/${APP_NAME}-${VERSION}.dmg"
# Remove existing DMG
if [ -f "$DMG_FILE" ]; then
rm -f "$DMG_FILE"
fi
# Check if create-dmg is available
if command -v create-dmg &> /dev/null; then
log_info "Using create-dmg for professional DMG..."
create-dmg \
--volname "$DMG_VOLUME_NAME" \
--icon-size 128 \
--window-size 512 400 \
--app-drop-link 380 200 \
"$DMG_FILE" \
"$DIST_DIR/$APP_NAME.app"
else
log_warning "create-dmg not found, using hdiutil (less stylish)"
log_info "For professional DMG: brew install create-dmg"
# Create temporary DMG directory structure
DMG_TEMP="$TEMP_BUILD/dmg_contents"
mkdir -p "$DMG_TEMP"
# Copy app bundle
cp -r "$DIST_DIR/$APP_NAME.app" "$DMG_TEMP/"
# Create symlink to Applications folder
ln -s /Applications "$DMG_TEMP/Applications"
# Create DMG
hdiutil create \
-volname "$DMG_VOLUME_NAME" \
-srcfolder "$DMG_TEMP" \
-ov \
-format UDZO \
"$DMG_FILE"
# Clean up
rm -rf "$DMG_TEMP"
fi
if [ ! -f "$DMG_FILE" ]; then
log_error "DMG file not created"
exit 1
fi
# Get file size
SIZE=$(du -h "$DMG_FILE" | cut -f1)
log_success "DMG created successfully"
log_info "Output: $DMG_FILE (Size: $SIZE)"
echo ""
}
sign_app() {
log_info "Signing application..."
echo ""
# Get signing identity from environment or use ad-hoc
SIGNING_ID="${APPLE_SIGNING_ID:--}"
codesign \
--deep \
--force \
--verify \
--verbose \
--options=runtime \
--sign "$SIGNING_ID" \
"$DIST_DIR/$APP_NAME.app"
if [ $? -ne 0 ]; then
log_error "Code signing failed"
exit 1
fi
log_success "Application signed successfully"
echo ""
}
notarize_app() {
log_info "Notarizing application..."
echo ""
# Requires:
# - APPLE_ID environment variable
# - APPLE_PASSWORD environment variable (app-specific password)
# - APPLE_TEAM_ID environment variable
if [ -z "$APPLE_ID" ] || [ -z "$APPLE_PASSWORD" ]; then
log_error "APPLE_ID and APPLE_PASSWORD environment variables required for notarization"
return 1
fi
DMG_FILE="$DIST_DIR/${APP_NAME}-${VERSION}.dmg"
# Upload for notarization
log_info "Uploading to Apple Notarization Service..."
xcrun notarytool submit "$DMG_FILE" \
--apple-id "$APPLE_ID" \
--password "$APPLE_PASSWORD" \
--team-id "${APPLE_TEAM_ID}" \
--wait
if [ $? -ne 0 ]; then
log_error "Notarization failed"
return 1
fi
# Staple the notarization
xcrun stapler staple "$DMG_FILE"
log_success "Application notarized successfully"
echo ""
}
# Run main function
main