292 lines
7.8 KiB
Bash
292 lines
7.8 KiB
Bash
|
|
#!/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 ""
|