LuckyWorld/scripts/mac_build.sh
Ozgur Ersoy ad5c5add9f
Some checks failed
Test macOS Build Action / test-macos-build (push) Has been cancelled
fix(actions): update notarization script to handle CI environment and improve certificate checks
2025-04-14 21:38:55 +02:00

316 lines
11 KiB
Bash
Executable File

#!/bin/bash
# Get the user's home directory
USER_HOME="$HOME"
# Set up Unreal Engine paths
UE_ROOT="/Users/Shared/Epic Games/UE_5.5"
UE_EDITOR="$UE_ROOT/Engine/Binaries/Mac/UnrealEditor.app/Contents/MacOS/UnrealEditor"
UE_UAT="$UE_ROOT/Engine/Build/BatchFiles/RunUAT.command"
# Set up project paths
PROJECT_ROOT="$(pwd)"
PROJECT_FILE="$PROJECT_ROOT/LuckyWorld.uproject"
ARCHIVE_DIR="$PROJECT_ROOT/Builds"
# Check if running in CI environment
if [ -n "$GITHUB_ACTIONS" ] || [ -n "$CI" ]; then
# Skip certificate check in CI environment
echo "🔄 Running in CI environment, skipping certificate checks"
RUNNING_IN_CI=true
else
RUNNING_IN_CI=false
fi
# Check for Developer ID certificate
CERTIFICATE_NAME=""
if [ "$RUNNING_IN_CI" = "false" ]; then
# Only check for certificate in non-CI environments
if [ -z "$CERTIFICATE_NAME" ]; then
# Try to find a Developer ID Application certificate
CERTIFICATE_NAME=$(security find-identity -v -p codesigning | grep "Developer ID Application" | head -1 | sed -E 's/.*"(Developer ID Application.*)"$/\1/')
if [ -z "$CERTIFICATE_NAME" ]; then
echo "⚠️ No Developer ID Application certificate found. Please specify a valid certificate name."
echo "Available certificates:"
security find-identity -v -p codesigning
echo "Continuing build without signing..."
else
echo "🔑 Found Developer ID certificate: $CERTIFICATE_NAME"
fi
fi
else
echo "🔄 Skipping local certificate check - signing will be handled in CI pipeline"
fi
# Check for entitlements file
if [ -f "$PROJECT_ROOT/LuckyWorld.entitlements" ]; then
ENTITLEMENTS_FILE="$PROJECT_ROOT/LuckyWorld.entitlements"
elif [ -f "$PROJECT_ROOT/LuckyRobots.entitlements" ]; then
ENTITLEMENTS_FILE="$PROJECT_ROOT/LuckyRobots.entitlements"
else
echo "Warning: No entitlements file found. This might affect notarization."
ENTITLEMENTS_FILE=""
fi
# For debugging: print paths and config
echo "Project root: $PROJECT_ROOT"
echo "Project file: $PROJECT_FILE"
echo "Archive directory: $ARCHIVE_DIR"
echo "Entitlements file: $ENTITLEMENTS_FILE"
if [ "$RUNNING_IN_CI" = "false" ] && [ -n "$CERTIFICATE_NAME" ]; then
echo "Signing with certificate: $CERTIFICATE_NAME"
else
echo "Not signing locally - will be handled in CI"
fi
# Clean up previous build artifacts
rm -rf DerivedDataCache Intermediate Binaries Saved
# Generate project files
"$UE_ROOT/Engine/Build/BatchFiles/Mac/GenerateProjectFiles.sh" -project="$PROJECT_FILE" -game -engine
# Run the build command
"$UE_UAT" -ScriptsForProject="$PROJECT_FILE" Turnkey \
-command=VerifySdk \
-platform=Mac \
-UpdateIfNeeded \
-EditorIO \
-EditorIOPort=59484 \
-project="$PROJECT_FILE" \
BuildCookRun \
-nop4 \
-utf8output \
-cook \
-project="$PROJECT_FILE" \
-target=LuckyWorld \
-unrealexe="$UE_EDITOR" \
-platform=Mac \
-installed \
-stage \
-archive \
-package \
-build \
-iterativecooking \
-pak \
-iostore \
-compressed \
-prereqs \
-archivedirectory="$ARCHIVE_DIR" \
-CrashReporter \
-clientconfig=Shipping \
# -nocompile \
# -nocompileuat \
# -nocompileeditor \
# -skipbuildeditor \
# enable these if you want to test build without pak and iostore (you're just testing the build)
# -skipiostore \
# -skippak \ (disable -pak and -iostore)
echo ""
echo "🦾 Build completed. Application path:"
APP_PATH=$(find "$ARCHIVE_DIR" -name "*.app" -type d | head -n 1)
echo "$APP_PATH"
if [ -n "$APP_PATH" ]; then
echo ""
echo "🔍 Binary files that will need signing:"
DYLIB_COUNT=$(find "$APP_PATH" -name "*.dylib" | wc -l)
SO_COUNT=$(find "$APP_PATH" -name "*.so" | wc -l)
FRAMEWORKS=$(find "$APP_PATH" -path "*.framework/*" -type f -perm +111 | wc -l)
EXECUTABLES=$(find "$APP_PATH" -type f -perm +111 -not -path "*.framework/*" -not -name "*.dylib" -not -name "*.so" | wc -l)
echo "- $DYLIB_COUNT .dylib libraries"
echo "- $SO_COUNT .so libraries"
echo "- $FRAMEWORKS framework executables"
echo "- $EXECUTABLES other executables"
echo "Total binary files: $((DYLIB_COUNT + SO_COUNT + FRAMEWORKS + EXECUTABLES))"
echo ""
echo "🔍 Checking for PhysX and other special libraries (often need special handling):"
find "$APP_PATH" -name "*PhysX*" -o -name "*APEX*"
fi
# Update bundle ID in project settings
echo ""
echo "🔧 Updating bundle ID in UE config..."
CONFIG_FILE="$PROJECT_ROOT/Config/DefaultGame.ini"
if [ -f "$CONFIG_FILE" ]; then
# Check if section exists or add it
if grep -q "\[/Script/MacTargetPlatform\.MacTargetSettings\]" "$CONFIG_FILE"; then
# Section exists, update the setting
sed -i '' 's/BundleIdentifier=.*/BundleIdentifier=com.luckyrobots.luckyworld/g' "$CONFIG_FILE"
else
# Section doesn't exist, add it
echo "" >> "$CONFIG_FILE"
echo "[/Script/MacTargetPlatform.MacTargetSettings]" >> "$CONFIG_FILE"
echo "BundleIdentifier=com.luckyrobots.luckyworld" >> "$CONFIG_FILE"
fi
echo "Updated bundle ID in project config"
fi
# Post-build process - set bundle ID
echo ""
echo "🔧 Performing post-build fix for bundle ID..."
if [ -n "$APP_PATH" ]; then
INFO_PLIST="$APP_PATH/Contents/Info.plist"
if [ -f "$INFO_PLIST" ]; then
echo "Setting bundle identifier to com.luckyrobots.luckyworld"
/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier com.luckyrobots.luckyworld" "$INFO_PLIST"
echo "Updated bundle ID: $(/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "$INFO_PLIST")"
else
echo "⚠️ Info.plist not found at $INFO_PLIST"
fi
fi
# Only perform signing if not in CI and certificate is available
if [ "$RUNNING_IN_CI" = "false" ] && [ -n "$CERTIFICATE_NAME" ] && [ -n "$ENTITLEMENTS_FILE" ]; then
# Recursive signing function - signs all binary files
function sign_recursively() {
local app_path="$1"
local entitlements_file="$2"
local certificate="$3"
local counter=0
local total=0
local failed=0
# First calculate total file count
# Find all binary files (executables, dylibs, .so files, frameworks)
echo "Scanning for binary files..."
# Executable binary files (libraries, executables)
binaries=$(find "$app_path" -type f \( -name "*.dylib" -o -name "*.so" -o -perm +111 \) | sort)
total=$(echo "$binaries" | wc -l)
echo "Found $total binary files to sign"
# Sign helper binary files (in order of preference)
echo "Signing all binary files (libraries and executables)..."
echo "$binaries" | while read -r binary; do
counter=$((counter + 1))
# Show progress every 20 files
if [ $((counter % 20)) -eq 0 ] || [ $counter -eq 1 ] || [ $counter -eq $total ]; then
echo "Progress: $counter/$total - Signing: $binary"
fi
# Skip if not a regular file (symbolic links etc)
if [ ! -f "$binary" ]; then
continue
fi
# Check file type
file_info=$(file "$binary")
# Only sign Mach-O files
if ! echo "$file_info" | grep -q "Mach-O"; then
continue
fi
if [[ "$binary" == *CrashReportClient* ]]; then
echo "🛠️ Special handling for CrashReportClient: $binary"
fi
# Sign with timestamp and runtime options
codesign --force --options runtime --deep --sign "$certificate" --timestamp --entitlements "$entitlements_file" "$binary" 2>&1 || {
echo "⚠️ Failed to sign: $binary"
failed=$((failed + 1))
}
done
# Show ENTITLEMENTS content for reference
echo "Using entitlements file for signatures:"
cat "$entitlements_file"
# Find all nested apps and sign them
nested_apps=$(find "$app_path" -name "*.app" -type d)
if [ -n "$nested_apps" ]; then
echo "Signing nested applications..."
echo "$nested_apps" | while read -r nested_app; do
if [ "$nested_app" != "$app_path" ]; then
echo "Signing nested app: $nested_app"
# Set Bundle ID in Info.plist if it exists before signing
nested_info="$nested_app/Contents/Info.plist"
if [ -f "$nested_info" ]; then
echo "Setting bundle identifier for nested app"
/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier com.luckyrobots.luckyworld.nested" "$nested_info" 2>/dev/null || true
fi
codesign --force --options runtime --deep --sign "$certificate" --timestamp --entitlements "$entitlements_file" "$nested_app" 2>&1 || {
echo "⚠️ Failed to sign nested app: $nested_app"
failed=$((failed + 1))
}
fi
done
fi
# Sign the main application
echo "Signing main application: $app_path"
codesign --force --options runtime --deep --sign "$certificate" --timestamp --entitlements "$entitlements_file" "$app_path" 2>&1 || {
echo "⚠️ Failed to sign main app: $app_path"
failed=$((failed + 1))
}
echo "✅ Signing completed: $counter files processed, $failed failures"
# Check signing status
echo "Verifying signatures..."
codesign -vvv --deep --strict "$app_path"
# Check Hardened Runtime and other security settings
echo "Checking security settings (Hardened Runtime, etc.):"
codesign -d --entitlements - "$app_path" | grep -i "runtime\|hardened\|security"
# Check CrashReportClient specifically (problematic file)
crash_reporter=$(find "$app_path" -path "*CrashReportClient.app/Contents/MacOS/CrashReportClient" -type f | head -1)
if [ -n "$crash_reporter" ]; then
echo "Checking CrashReportClient specifically:"
codesign -d --entitlements - "$crash_reporter" | grep -i "runtime\|hardened\|security"
fi
}
# Check libraries and perform post-processing if needed
echo ""
echo "🔍 Performing comprehensive signing and hardening of all binaries..."
# Sign all binary files recursively
sign_recursively "$APP_PATH" "$ENTITLEMENTS_FILE" "$CERTIFICATE_NAME"
# Final signing of the main app bundle
echo "Final signing of main app bundle"
codesign --force --options runtime --deep --sign "$CERTIFICATE_NAME" --timestamp --entitlements "$ENTITLEMENTS_FILE" "$APP_PATH"
echo "✅ All binaries signed successfully with Hardened Runtime enabled"
# Prepare app for notarization
echo ""
echo "🔐 Preparing for notarization..."
# Create a ZIP archive for notarization
ZIP_PATH="$ARCHIVE_DIR/LuckyWorld.zip"
echo "Creating ZIP archive for notarization: $ZIP_PATH"
ditto -c -k --keepParent "$APP_PATH" "$ZIP_PATH"
echo "✅ ZIP archive created for notarization at: $ZIP_PATH"
echo ""
echo "To notarize the app, run the following command:"
echo "xcrun notarytool submit \"$ZIP_PATH\" --apple-id \"YOUR_APPLE_ID\" --password \"APP_SPECIFIC_PASSWORD\" --team-id \"YOUR_TEAM_ID\" --wait"
echo ""
else
# Skip signing locally - CI will handle it
if [ "$RUNNING_IN_CI" = "true" ]; then
echo "Skipping local signing - CI pipeline will handle signing and notarization"
else
echo "❌ Local signing skipped - certificate or entitlements file not available"
echo "App path: $APP_PATH"
echo "Entitlements file: $ENTITLEMENTS_FILE"
echo "Certificate: $CERTIFICATE_NAME"
fi
fi
echo ""
echo "✅ Build and post-processing completed successfully!"