#!/bin/bash # Production Build Verification Script # Tests the built .app bundle before distribution set -e APP_BUNDLE="${1:-build/macos/Archipelago.app}" echo "🔍 Archipelago Production Build Verification" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" # Color codes RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color FAILED=0 WARNINGS=0 pass() { echo -e "${GREEN}✅ $1${NC}" } fail() { echo -e "${RED}❌ $1${NC}" FAILED=$((FAILED + 1)) } warn() { echo -e "${YELLOW}⚠️ $1${NC}" WARNINGS=$((WARNINGS + 1)) } # Check app bundle exists echo "📦 Checking app bundle..." if [ ! -d "$APP_BUNDLE" ]; then fail "App bundle not found at: $APP_BUNDLE" exit 1 fi pass "App bundle exists" # Check bundle structure echo "" echo "🏗️ Checking bundle structure..." if [ -d "$APP_BUNDLE/Contents/MacOS" ]; then pass "MacOS directory exists" else fail "MacOS directory missing" fi if [ -d "$APP_BUNDLE/Contents/Resources" ]; then pass "Resources directory exists" else fail "Resources directory missing" fi if [ -f "$APP_BUNDLE/Contents/Info.plist" ]; then pass "Info.plist exists" else fail "Info.plist missing" fi if [ -f "$APP_BUNDLE/Contents/PkgInfo" ]; then pass "PkgInfo exists" else fail "PkgInfo missing" fi # Check executables echo "" echo "⚙️ Checking executables..." if [ -f "$APP_BUNDLE/Contents/MacOS/archipelago" ]; then pass "Backend binary exists" # Check if executable if [ -x "$APP_BUNDLE/Contents/MacOS/archipelago" ]; then pass "Backend binary is executable" else fail "Backend binary is not executable" fi # Check architecture ARCH=$(lipo -info "$APP_BUNDLE/Contents/MacOS/archipelago" 2>&1) echo " Architecture: $ARCH" else fail "Backend binary missing" fi if [ -f "$APP_BUNDLE/Contents/MacOS/launch.sh" ]; then pass "Launch script exists" if [ -x "$APP_BUNDLE/Contents/MacOS/launch.sh" ]; then pass "Launch script is executable" else fail "Launch script is not executable" fi else fail "Launch script missing" fi if [ -f "$APP_BUNDLE/Contents/MacOS/manage-docker.sh" ]; then pass "Docker manager script exists" else warn "Docker manager script missing (optional)" fi # Check resources echo "" echo "📁 Checking resources..." if [ -d "$APP_BUNDLE/Contents/Resources/frontend" ]; then pass "Frontend directory exists" if [ -f "$APP_BUNDLE/Contents/Resources/frontend/index.html" ]; then pass "Frontend index.html exists" else fail "Frontend index.html missing" fi else fail "Frontend directory missing" fi if [ -d "$APP_BUNDLE/Contents/Resources/docker-ui" ]; then pass "Docker UI directory exists" if [ -d "$APP_BUNDLE/Contents/Resources/docker-ui/bitcoin-ui" ]; then pass "Bitcoin UI exists" else warn "Bitcoin UI missing" fi if [ -d "$APP_BUNDLE/Contents/Resources/docker-ui/lnd-ui" ]; then pass "LND UI exists" else warn "LND UI missing" fi else warn "Docker UI directory missing (optional)" fi if [ -f "$APP_BUNDLE/Contents/Resources/docker-compose.yml" ]; then pass "Docker compose file exists" else warn "Docker compose file missing (optional)" fi if [ -f "$APP_BUNDLE/Contents/Resources/AppIcon.icns" ]; then pass "App icon exists" else warn "App icon missing - will use default" fi # Check Info.plist content echo "" echo "📄 Validating Info.plist..." BUNDLE_ID=$(defaults read "$APP_BUNDLE/Contents/Info.plist" CFBundleIdentifier 2>/dev/null) if [ -n "$BUNDLE_ID" ]; then pass "Bundle ID: $BUNDLE_ID" else fail "Bundle ID not found" fi VERSION=$(defaults read "$APP_BUNDLE/Contents/Info.plist" CFBundleShortVersionString 2>/dev/null) if [ -n "$VERSION" ]; then pass "Version: $VERSION" else warn "Version not found" fi EXECUTABLE=$(defaults read "$APP_BUNDLE/Contents/Info.plist" CFBundleExecutable 2>/dev/null) if [ -n "$EXECUTABLE" ]; then pass "Executable: $EXECUTABLE" else fail "Executable not specified" fi # Check code signature echo "" echo "🔐 Checking code signature..." if codesign -v "$APP_BUNDLE" 2>/dev/null; then pass "App is code signed" # Get signature info SIGNATURE=$(codesign -dvv "$APP_BUNDLE" 2>&1 | grep "Authority=" | head -1) echo " $SIGNATURE" # Check if hardened runtime is enabled if codesign -dvv "$APP_BUNDLE" 2>&1 | grep -q "flags=0x10000"; then pass "Hardened runtime enabled" else warn "Hardened runtime not enabled (recommended for notarization)" fi else warn "App is not code signed (required for distribution)" fi # Check notarization echo "" echo "📝 Checking notarization..." if spctl --assess --type execute --verbose "$APP_BUNDLE" 2>&1 | grep -q "accepted"; then pass "App is notarized" else warn "App is not notarized (required for macOS 10.15+)" fi # Check for common issues echo "" echo "🔍 Checking for common issues..." # Check for hardcoded paths if grep -r "/Users/" "$APP_BUNDLE/Contents/MacOS/" 2>/dev/null | grep -v "Library/Application" >/dev/null; then warn "Found hardcoded user paths in binaries" fi # Check for debug symbols (should be stripped) if nm "$APP_BUNDLE/Contents/MacOS/archipelago" 2>/dev/null | grep -q "debug"; then warn "Debug symbols present - consider stripping for smaller size" fi # Check bundle size echo "" echo "📏 Bundle size..." BUNDLE_SIZE=$(du -sh "$APP_BUNDLE" | cut -f1) echo " Total size: $BUNDLE_SIZE" if [ -d "$APP_BUNDLE/Contents/Resources/frontend" ]; then FRONTEND_SIZE=$(du -sh "$APP_BUNDLE/Contents/Resources/frontend" | cut -f1) echo " Frontend: $FRONTEND_SIZE" fi BACKEND_SIZE=$(du -sh "$APP_BUNDLE/Contents/MacOS/archipelago" | cut -f1) echo " Backend: $BACKEND_SIZE" # Summary echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "📊 Verification Summary" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" if [ $FAILED -eq 0 ]; then if [ $WARNINGS -eq 0 ]; then pass "All checks passed! 🎉" echo "" echo "✅ Ready for distribution" else echo -e "${YELLOW}⚠️ Passed with $WARNINGS warning(s)${NC}" echo "" echo "Build is usable but has some warnings (see above)" fi else echo -e "${RED}❌ Failed: $FAILED critical issue(s)${NC}" echo -e "${YELLOW}⚠️ Warnings: $WARNINGS${NC}" echo "" echo "⛔ Build has critical issues - DO NOT distribute" exit 1 fi # Next steps echo "" echo "🚀 Next Steps:" echo "" if codesign -v "$APP_BUNDLE" 2>/dev/null && spctl --assess --type execute "$APP_BUNDLE" 2>&1 | grep -q "accepted"; then echo " 1. ✅ App is signed and notarized" echo " 2. Create DMG: hdiutil create -volname \"Archipelago\" \\" echo " -srcfolder \"$APP_BUNDLE\" \\" echo " -ov -format UDZO Archipelago.dmg" echo " 3. Distribute via GitHub Releases or website" else echo " 1. Code sign the app:" echo " codesign --deep --force --verify --verbose \\" echo " --sign \"Developer ID Application: Your Name\" \\" echo " --options runtime \"$APP_BUNDLE\"" echo "" echo " 2. Notarize the app:" echo " ditto -c -k --keepParent \"$APP_BUNDLE\" Archipelago.zip" echo " xcrun notarytool submit Archipelago.zip ..." echo "" echo " 3. Create DMG after notarization" fi echo "" echo "📖 For detailed instructions, see BUILD_MACOS.md" echo ""