archy/build-macos-production.sh

291 lines
10 KiB
Bash
Raw Normal View History

#!/bin/bash
# Archipelago Production macOS Build Script
# Creates a production-ready .app bundle and .dmg installer
set -e
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
APP_NAME="Archipelago"
APP_VERSION="${ARCHIPELAGO_VERSION:-0.1.0}"
BUILD_DIR="$PROJECT_ROOT/build/macos"
APP_BUNDLE="$BUILD_DIR/$APP_NAME.app"
DMG_NAME="Archipelago-${APP_VERSION}-macOS.dmg"
echo "🏗️ Archipelago macOS Production Build"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " Version: $APP_VERSION"
echo " Target: macOS App Bundle + DMG Installer"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# Clean previous build
echo "🧹 Cleaning previous build..."
rm -rf "$BUILD_DIR"
mkdir -p "$BUILD_DIR"
# Step 1: Build Rust Backend (Release Mode)
echo ""
echo "⚙️ Step 1/6: Building Rust backend (release mode)..."
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
cd "$PROJECT_ROOT/core"
cargo build --release --workspace
if [ ! -f "target/release/archipelago" ]; then
echo "❌ Backend build failed - archipelago binary not found"
exit 1
fi
# Get binary size
BACKEND_SIZE=$(du -h target/release/archipelago | cut -f1)
echo "✅ Backend built successfully ($BACKEND_SIZE)"
# Step 2: Build Vue.js Frontend (Production Mode)
echo ""
echo "🎨 Step 2/6: Building Vue.js frontend (production mode)..."
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
cd "$PROJECT_ROOT/neode-ui"
# Check if node_modules exists
if [ ! -d "node_modules" ]; then
echo "📦 Installing frontend dependencies..."
npm install
fi
# Build production frontend
npm run build
if [ ! -d "dist" ]; then
echo "❌ Frontend build failed - dist directory not found"
exit 1
fi
# Get build size
FRONTEND_SIZE=$(du -sh dist | cut -f1)
echo "✅ Frontend built successfully ($FRONTEND_SIZE)"
# Step 3: Create macOS App Bundle Structure
echo ""
echo "📦 Step 3/6: Creating macOS app bundle..."
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
# Create standard macOS .app directory structure
mkdir -p "$APP_BUNDLE/Contents/MacOS"
mkdir -p "$APP_BUNDLE/Contents/Resources"
mkdir -p "$APP_BUNDLE/Contents/Frameworks"
# Copy backend binary
echo " • Copying backend binary..."
cp "$PROJECT_ROOT/core/target/release/archipelago" "$APP_BUNDLE/Contents/MacOS/"
chmod +x "$APP_BUNDLE/Contents/MacOS/archipelago"
# Copy frontend build
echo " • Copying frontend assets..."
cp -R "$PROJECT_ROOT/neode-ui/dist" "$APP_BUNDLE/Contents/Resources/frontend"
# Copy Docker UI assets
echo " • Copying Docker UI assets..."
mkdir -p "$APP_BUNDLE/Contents/Resources/docker-ui"
cp -R "$PROJECT_ROOT/docker/bitcoin-ui" "$APP_BUNDLE/Contents/Resources/docker-ui/"
cp -R "$PROJECT_ROOT/docker/lnd-ui" "$APP_BUNDLE/Contents/Resources/docker-ui/"
# Copy configuration templates
echo " • Copying configuration..."
cp "$PROJECT_ROOT/core/.env.example" "$APP_BUNDLE/Contents/Resources/env.template"
cp "$PROJECT_ROOT/core/.env.production" "$APP_BUNDLE/Contents/Resources/env.production"
# Copy docker-compose.yml for production
echo " • Copying Docker configuration..."
cp "$PROJECT_ROOT/docker-compose.yml" "$APP_BUNDLE/Contents/Resources/"
cp "$PROJECT_ROOT/manage-docker.sh" "$APP_BUNDLE/Contents/MacOS/"
chmod +x "$APP_BUNDLE/Contents/MacOS/manage-docker.sh"
# Create launch script
echo " • Creating launcher script..."
cat > "$APP_BUNDLE/Contents/MacOS/launch.sh" << 'LAUNCH_EOF'
#!/bin/bash
# Archipelago macOS Launcher
# Get the directory containing this script
BUNDLE_DIR="$(cd "$(dirname "$0")/.." && pwd)"
RESOURCES_DIR="$BUNDLE_DIR/Resources"
MACOS_DIR="$BUNDLE_DIR/MacOS"
# Set up data directory in user's home
DATA_DIR="$HOME/Library/Application Support/Archipelago"
mkdir -p "$DATA_DIR/data"
mkdir -p "$DATA_DIR/logs"
# Export environment variables
export ARCHIPELAGO_DATA_DIR="$DATA_DIR/data"
export ARCHIPELAGO_FRONTEND_DIR="$RESOURCES_DIR/frontend"
export ARCHIPELAGO_DOCKER_UI_DIR="$RESOURCES_DIR/docker-ui"
export ARCHIPELAGO_LOG_DIR="$DATA_DIR/logs"
export RUST_LOG="${RUST_LOG:-info}"
# Launch backend
cd "$DATA_DIR"
exec "$MACOS_DIR/archipelago" > "$DATA_DIR/logs/archipelago.log" 2>&1
LAUNCH_EOF
chmod +x "$APP_BUNDLE/Contents/MacOS/launch.sh"
# Step 4: Create Info.plist
echo ""
echo "📄 Step 4/6: Creating app metadata..."
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
cat > "$APP_BUNDLE/Contents/Info.plist" << PLIST_EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleName</key>
<string>Archipelago</string>
<key>CFBundleDisplayName</key>
<string>Archipelago</string>
<key>CFBundleIdentifier</key>
<string>com.archipelago.app</string>
<key>CFBundleVersion</key>
<string>$APP_VERSION</string>
<key>CFBundleShortVersionString</key>
<string>$APP_VERSION</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>ARCH</string>
<key>CFBundleExecutable</key>
<string>launch.sh</string>
<key>CFBundleIconFile</key>
<string>AppIcon</string>
<key>NSHighResolutionCapable</key>
<true/>
<key>LSMinimumSystemVersion</key>
<string>10.15</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2026 Archipelago. All rights reserved.</string>
<key>LSBackgroundOnly</key>
<false/>
<key>NSRequiresAquaSystemAppearance</key>
<false/>
<key>NSSupportsAutomaticGraphicsSwitching</key>
<true/>
</dict>
</plist>
PLIST_EOF
echo "✅ Info.plist created"
# Create PkgInfo
echo -n "APPLARCH" > "$APP_BUNDLE/Contents/PkgInfo"
# Step 5: Create App Icon (placeholder - user should provide real icon)
echo ""
echo "🎨 Step 5/6: Creating app icon..."
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
# Check if sips command is available (macOS built-in)
if command -v sips >/dev/null 2>&1; then
# Try to find a logo to convert
LOGO_SOURCE=""
if [ -f "$PROJECT_ROOT/neode-ui/public/assets/img/logo.png" ]; then
LOGO_SOURCE="$PROJECT_ROOT/neode-ui/public/assets/img/logo.png"
elif [ -f "$PROJECT_ROOT/neode-ui/public/assets/img/app-icons/bitcoin-core.png" ]; then
LOGO_SOURCE="$PROJECT_ROOT/neode-ui/public/assets/img/app-icons/bitcoin-core.png"
fi
if [ -n "$LOGO_SOURCE" ]; then
# Create iconset
ICONSET_DIR="$BUILD_DIR/AppIcon.iconset"
mkdir -p "$ICONSET_DIR"
# Generate icon sizes
for size in 16 32 128 256 512; do
sips -z $size $size "$LOGO_SOURCE" --out "$ICONSET_DIR/icon_${size}x${size}.png" >/dev/null 2>&1
sips -z $((size*2)) $((size*2)) "$LOGO_SOURCE" --out "$ICONSET_DIR/icon_${size}x${size}@2x.png" >/dev/null 2>&1
done
# Convert to icns
iconutil -c icns "$ICONSET_DIR" -o "$APP_BUNDLE/Contents/Resources/AppIcon.icns" 2>/dev/null || {
echo "⚠️ Icon conversion failed - app will use default icon"
}
rm -rf "$ICONSET_DIR"
echo "✅ App icon created from $LOGO_SOURCE"
else
echo "⚠️ No logo found - app will use default icon"
echo " Add logo.png to neode-ui/public/assets/img/ and rebuild"
fi
else
echo "⚠️ sips not available - skipping icon creation"
fi
# Step 6: Create DMG Installer
echo ""
echo "💿 Step 6/6: Creating DMG installer..."
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
DMG_TEMP_DIR="$BUILD_DIR/dmg"
mkdir -p "$DMG_TEMP_DIR"
# Copy app to DMG staging
cp -R "$APP_BUNDLE" "$DMG_TEMP_DIR/"
# Create Applications symlink
ln -s /Applications "$DMG_TEMP_DIR/Applications"
# Create DMG
hdiutil create -volname "Archipelago $APP_VERSION" \
-srcfolder "$DMG_TEMP_DIR" \
-ov -format UDZO \
"$BUILD_DIR/$DMG_NAME" 2>/dev/null || {
echo "⚠️ DMG creation failed - using app bundle only"
}
# Cleanup
rm -rf "$DMG_TEMP_DIR"
# Summary
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "✅ Production build complete!"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "📦 Build artifacts:"
echo " • App Bundle: $APP_BUNDLE"
if [ -f "$BUILD_DIR/$DMG_NAME" ]; then
DMG_SIZE=$(du -h "$BUILD_DIR/$DMG_NAME" | cut -f1)
echo " • DMG Installer: $BUILD_DIR/$DMG_NAME ($DMG_SIZE)"
fi
echo ""
echo "📋 Build summary:"
echo " • Backend: $BACKEND_SIZE (Rust)"
echo " • Frontend: $FRONTEND_SIZE (Vue.js)"
BUNDLE_SIZE=$(du -sh "$APP_BUNDLE" | cut -f1)
echo " • Total Bundle: $BUNDLE_SIZE"
echo ""
echo "🚀 Next steps:"
echo " 1. Test the app:"
echo " open \"$APP_BUNDLE\""
echo ""
echo " 2. Install system-wide:"
echo " cp -R \"$APP_BUNDLE\" /Applications/"
echo ""
echo " 3. Distribute via DMG:"
if [ -f "$BUILD_DIR/$DMG_NAME" ]; then
echo " • Share: $BUILD_DIR/$DMG_NAME"
else
echo " • (DMG creation skipped - use app bundle directly)"
fi
echo ""
echo " 4. Code signing (optional but recommended):"
echo " codesign --deep --force --verify --verbose \\
--sign \"Developer ID Application: Your Name\" \\
\"$APP_BUNDLE\""
echo ""
echo "💡 For notarization (macOS 10.14.5+):"
echo " • Requires Apple Developer account"
echo " • Use: xcrun notarytool submit $DMG_NAME ..."
echo ""