Some checks failed
Test macOS Build Action / test-macos-build (push) Failing after 33m43s
888 lines
37 KiB
YAML
888 lines
37 KiB
YAML
name: Test macOS Build Action
|
|
|
|
on:
|
|
workflow_dispatch: # Manual trigger only for testing
|
|
push:
|
|
branches: [ozgur/build]
|
|
|
|
jobs:
|
|
test-macos-build:
|
|
runs-on: macos
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v3
|
|
with:
|
|
lfs: true
|
|
fetch-depth: 0
|
|
|
|
# Enable debug logging
|
|
- name: Enable Debug Logging
|
|
run: |
|
|
echo "ACTIONS_RUNNER_DEBUG=true" >> $GITHUB_ENV
|
|
echo "ACTIONS_STEP_DEBUG=true" >> $GITHUB_ENV
|
|
shell: bash
|
|
|
|
# Setup environment for build
|
|
- name: Setup environment
|
|
run: |
|
|
# Get the working directory path for absolute paths
|
|
WORKSPACE_DIR="$(pwd)"
|
|
echo "WORKSPACE_DIR=$WORKSPACE_DIR" >> "$GITHUB_ENV"
|
|
echo "ENTITLEMENTS_FILE=LuckyWorld.entitlements" >> "$GITHUB_ENV"
|
|
|
|
# Set CI environment variable to true for build script
|
|
echo "CI=true" >> "$GITHUB_ENV"
|
|
|
|
# Create directories for builds
|
|
mkdir -p Builds/Mac
|
|
mkdir -p PackagedReleases
|
|
mkdir -p ArchivedApps
|
|
|
|
echo "Environment setup complete"
|
|
shell: bash
|
|
|
|
# Build for macOS - use your own build script
|
|
- name: Build for macOS
|
|
run: |
|
|
if [ -f "./scripts/mac_build.sh" ]; then
|
|
chmod +x ./scripts/mac_build.sh
|
|
# Set CI environment variable explicitly before running
|
|
export CI=true
|
|
./scripts/mac_build.sh
|
|
|
|
# Check if the build succeeded by looking for the app
|
|
APP_PATHS=$(find ./Builds -type d -name "*.app" 2>/dev/null || echo "")
|
|
if [ -z "$APP_PATHS" ]; then
|
|
APP_PATHS=$(find ./Saved/StagedBuilds -type d -name "*.app" 2>/dev/null || echo "")
|
|
fi
|
|
|
|
if [ -z "$APP_PATHS" ]; then
|
|
echo "❌ ERROR: Build command appeared to succeed but no app bundle was found!"
|
|
echo "This usually means the build failed but didn't properly return an error code."
|
|
exit 1
|
|
fi
|
|
else
|
|
echo "ERROR: Build script not found at ./scripts/mac_build.sh"
|
|
exit 1
|
|
fi
|
|
shell: bash
|
|
|
|
# Find the app bundle
|
|
- name: Find app bundle
|
|
run: |
|
|
# Add error handling
|
|
set +e # Don't exit immediately on error for this block
|
|
|
|
echo "Build status check..."
|
|
if [ ! -d "./Builds" ] && [ ! -d "./Saved/StagedBuilds" ]; then
|
|
echo "❌ ERROR: Build directories do not exist. Build likely failed."
|
|
exit 1
|
|
fi
|
|
|
|
# First check Saved/StagedBuilds directory - where Unreal often places built apps
|
|
echo "Checking Saved/StagedBuilds directory..."
|
|
APP_PATHS=$(find ./Saved/StagedBuilds -type d -name "*.app" 2>/dev/null || echo "")
|
|
|
|
# If not found, check Builds directory
|
|
if [ -z "$APP_PATHS" ]; then
|
|
echo "No app found in Saved/StagedBuilds, checking Builds directory..."
|
|
APP_PATHS=$(find ./Builds -type d -name "*.app" 2>/dev/null || echo "")
|
|
fi
|
|
|
|
# If still not found, check the whole workspace
|
|
if [ -z "$APP_PATHS" ]; then
|
|
echo "No app found in Builds, checking entire workspace..."
|
|
APP_PATHS=$(find . -type d -name "*.app" -not -path "*/\.*" 2>/dev/null || echo "")
|
|
fi
|
|
|
|
if [ -z "$APP_PATHS" ]; then
|
|
echo "❌ ERROR: Could not find any app bundles!"
|
|
echo "Listing all directories to help debug:"
|
|
find . -type d -maxdepth 3 | sort
|
|
exit 1
|
|
fi
|
|
|
|
echo "Found potential app bundles:"
|
|
echo "$APP_PATHS"
|
|
|
|
# Use the first app path found (preferably the main app, not a child app)
|
|
MAIN_APP_PATH=$(echo "$APP_PATHS" | grep -v "CrashReportClient" | head -1 || echo "$APP_PATHS" | head -1)
|
|
|
|
echo "Using app bundle: $MAIN_APP_PATH"
|
|
echo "APP_PATH=$MAIN_APP_PATH" >> "$GITHUB_ENV"
|
|
|
|
# Make sure app exists - using local variable
|
|
if [ ! -d "$MAIN_APP_PATH" ]; then
|
|
echo "❌ ERROR: App bundle not found at $MAIN_APP_PATH!"
|
|
exit 1
|
|
fi
|
|
|
|
# Export APP_PATH for next steps to use
|
|
echo "APP_PATH=$MAIN_APP_PATH" >> "$GITHUB_ENV"
|
|
|
|
# Get bundle ID from Info.plist for reference (not modifying)
|
|
if [ -f "$MAIN_APP_PATH/Contents/Info.plist" ]; then
|
|
BUNDLE_ID=$(/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "$MAIN_APP_PATH/Contents/Info.plist")
|
|
echo "Detected bundle ID: $BUNDLE_ID"
|
|
echo "BUNDLE_ID=$BUNDLE_ID" >> "$GITHUB_ENV"
|
|
fi
|
|
shell: bash
|
|
|
|
# Basic pre-notarization checks
|
|
- name: Check for notarization issues
|
|
run: |
|
|
echo "🔍 Checking app for potential notarization issues..."
|
|
|
|
APP_PATH="${{ env.APP_PATH }}"
|
|
|
|
# Verify code signature already exists (from Unreal build)
|
|
echo "Checking existing signature..."
|
|
codesign -vvv "$APP_PATH" || echo "⚠️ App may not be properly signed by Unreal Engine"
|
|
|
|
# Check for any ad-hoc signatures that would cause issues
|
|
if codesign -dvv "$APP_PATH" 2>&1 | grep -q "adhoc"; then
|
|
echo "⚠️ Warning: Ad-hoc signature detected. This will be replaced with a proper signature."
|
|
fi
|
|
|
|
# Verify entitlements file exists
|
|
if [ ! -f "${{ env.ENTITLEMENTS_FILE }}" ]; then
|
|
echo "⚠️ Entitlements file not found. Will use default entitlements."
|
|
else
|
|
echo "Found entitlements file: ${{ env.ENTITLEMENTS_FILE }}"
|
|
fi
|
|
shell: bash
|
|
|
|
# Create a debug log file for notarize action
|
|
- name: Create debug log directory
|
|
run: |
|
|
mkdir -p debug_logs
|
|
echo "DEBUG_LOG_PATH=$(pwd)/debug_logs/notarize_log.txt" >> $GITHUB_ENV
|
|
shell: bash
|
|
|
|
# Beginning of macos-notarize steps
|
|
- name: Setup debug environment
|
|
id: setup-debug-env
|
|
run: |
|
|
# Create debug directory if env variable is set
|
|
if [[ -n "$DEBUG_LOG_PATH" ]]; then
|
|
mkdir -p "$(dirname "$DEBUG_LOG_PATH")"
|
|
touch "$DEBUG_LOG_PATH"
|
|
echo "Debug logging enabled to: $DEBUG_LOG_PATH" | tee -a "$DEBUG_LOG_PATH"
|
|
fi
|
|
|
|
# Define debug log helper function
|
|
echo '
|
|
# Helper function for debug logging
|
|
function debug_log() {
|
|
echo "DEBUG: $1"
|
|
if [[ -n "$DEBUG_LOG_PATH" ]]; then
|
|
echo "$(date "+%Y-%m-%d %H:%M:%S") - $1" >> "$DEBUG_LOG_PATH"
|
|
fi
|
|
}
|
|
' > /tmp/debug_helpers.sh
|
|
chmod +x /tmp/debug_helpers.sh
|
|
|
|
# Log debug message directly
|
|
echo "DEBUG: Starting macOS notarize action"
|
|
echo "$(date "+%Y-%m-%d %H:%M:%S") - Starting macOS notarize action" >> "$DEBUG_LOG_PATH"
|
|
echo "DEBUG: App path: ${{ env.APP_PATH }}"
|
|
echo "$(date "+%Y-%m-%d %H:%M:%S") - App path: ${{ env.APP_PATH }}" >> "$DEBUG_LOG_PATH"
|
|
echo "DEBUG: Team ID: ${{ secrets.APPLE_TEAM_ID }}"
|
|
echo "$(date "+%Y-%m-%d %H:%M:%S") - Team ID: ${{ secrets.APPLE_TEAM_ID }}" >> "$DEBUG_LOG_PATH"
|
|
echo "DEBUG: Notarization method: api-key"
|
|
echo "$(date "+%Y-%m-%d %H:%M:%S") - Notarization method: api-key" >> "$DEBUG_LOG_PATH"
|
|
echo "DEBUG: Bundle ID: ${{ env.BUNDLE_ID }}"
|
|
echo "$(date "+%Y-%m-%d %H:%M:%S") - Bundle ID: ${{ env.BUNDLE_ID }}" >> "$DEBUG_LOG_PATH"
|
|
shell: bash
|
|
|
|
- name: Set up variables
|
|
id: setup
|
|
run: |
|
|
# Debug log helper
|
|
function debug_log() {
|
|
echo "DEBUG: $1"
|
|
if [[ -n "$DEBUG_LOG_PATH" ]]; then
|
|
echo "$(date "+%Y-%m-%d %H:%M:%S") - $1" >> "$DEBUG_LOG_PATH"
|
|
fi
|
|
}
|
|
|
|
# Debugging info
|
|
debug_log "Setting up variables"
|
|
|
|
# Generate unique name for keychain
|
|
KEYCHAIN_NAME="build-keychain-$(uuidgen)"
|
|
KEYCHAIN_PASSWORD="$(uuidgen)"
|
|
echo "KEYCHAIN_NAME=$KEYCHAIN_NAME" >> $GITHUB_ENV
|
|
echo "KEYCHAIN_PASSWORD=$KEYCHAIN_PASSWORD" >> $GITHUB_ENV
|
|
|
|
# Set paths
|
|
echo "APP_PATH=${{ env.APP_PATH }}" >> $GITHUB_ENV
|
|
|
|
# Generate working directory for temp files
|
|
WORK_DIR="$(mktemp -d)"
|
|
echo "WORK_DIR=$WORK_DIR" >> $GITHUB_ENV
|
|
|
|
# Set bundle id (from input or extract from app)
|
|
if [[ -n "${{ env.BUNDLE_ID }}" ]]; then
|
|
BUNDLE_ID="${{ env.BUNDLE_ID }}"
|
|
else
|
|
BUNDLE_ID=$(/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "${{ env.APP_PATH }}/Contents/Info.plist")
|
|
fi
|
|
echo "BUNDLE_ID=$BUNDLE_ID" >> $GITHUB_ENV
|
|
|
|
# Get app name from bundle path
|
|
APP_NAME=$(basename "${{ env.APP_PATH }}" .app)
|
|
echo "APP_NAME=$APP_NAME" >> $GITHUB_ENV
|
|
|
|
# Set output directory
|
|
OUTPUT_DIR="$(pwd)/PackagedReleases"
|
|
mkdir -p "$OUTPUT_DIR"
|
|
echo "OUTPUT_DIR=$OUTPUT_DIR" >> $GITHUB_ENV
|
|
|
|
# Set package paths
|
|
ZIP_PATH="$OUTPUT_DIR/${APP_NAME}.zip"
|
|
DMG_PATH="$OUTPUT_DIR/${APP_NAME}.dmg"
|
|
echo "ZIP_PATH=$ZIP_PATH" >> $GITHUB_ENV
|
|
echo "DMG_PATH=$DMG_PATH" >> $GITHUB_ENV
|
|
|
|
# Set notarization variables based on method
|
|
debug_log "Using API key method for notarization"
|
|
|
|
# Create API key file - properly decode from base64
|
|
API_KEY_FILE="$WORK_DIR/api_key.p8"
|
|
debug_log "Creating API key file at: $API_KEY_FILE"
|
|
|
|
# Check if NOTARY_API_KEY_PATH is provided
|
|
if [[ -z "${{ secrets.NOTARY_API_KEY_PATH }}" ]]; then
|
|
debug_log "ERROR: NOTARY_API_KEY_PATH secret is empty"
|
|
exit 1
|
|
fi
|
|
|
|
# First try using the secret directly (assuming it's a PEM key directly)
|
|
echo "${{ secrets.NOTARY_API_KEY_PATH }}" > "$API_KEY_FILE"
|
|
|
|
# Check if it's already in PEM format
|
|
if grep -q "BEGIN PRIVATE KEY" "$API_KEY_FILE"; then
|
|
debug_log "Secret is already in PEM format, using directly"
|
|
else
|
|
debug_log "Secret is not in PEM format, trying to decode as base64"
|
|
# Try base64 decoding
|
|
echo "${{ secrets.NOTARY_API_KEY_PATH }}" | base64 -D > "$API_KEY_FILE.decoded" 2>/dev/null || true
|
|
|
|
# Check if decoded content is PEM
|
|
if [[ -s "$API_KEY_FILE.decoded" ]] && grep -q "BEGIN PRIVATE KEY" "$API_KEY_FILE.decoded"; then
|
|
debug_log "Successfully decoded secret from base64 to PEM"
|
|
mv "$API_KEY_FILE.decoded" "$API_KEY_FILE"
|
|
else
|
|
debug_log "ERROR: Secret is neither PEM nor valid base64-encoded PEM"
|
|
debug_log "Secret starts with: $(head -c 20 "$API_KEY_FILE" | xxd -p)"
|
|
if [[ -f "$API_KEY_FILE.decoded" ]]; then
|
|
debug_log "Decoded content starts with: $(head -c 20 "$API_KEY_FILE.decoded" | xxd -p)"
|
|
fi
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# Verify API key file exists and has content
|
|
if [[ ! -f "$API_KEY_FILE" ]]; then
|
|
debug_log "ERROR: API key file could not be created"
|
|
exit 1
|
|
fi
|
|
|
|
if [[ ! -s "$API_KEY_FILE" ]]; then
|
|
debug_log "ERROR: API key file is empty"
|
|
exit 1
|
|
fi
|
|
|
|
# Get file permissions and size for debugging
|
|
FILE_PERMS=$(ls -la "$API_KEY_FILE")
|
|
FILE_SIZE=$(wc -c < "$API_KEY_FILE")
|
|
debug_log "API key file ($FILE_SIZE bytes): $FILE_PERMS"
|
|
|
|
# Set proper permissions
|
|
chmod 600 "$API_KEY_FILE"
|
|
|
|
echo "API_KEY_FILE=$API_KEY_FILE" >> $GITHUB_ENV
|
|
|
|
debug_log "API key file created at: $API_KEY_FILE"
|
|
debug_log "API key ID: ${{ secrets.NOTARY_API_KEY_ID }}"
|
|
debug_log "API key issuer ID: ${{ secrets.NOTARY_API_KEY_ISSUER_ID }}"
|
|
shell: bash
|
|
|
|
- name: Setup keychain
|
|
id: setup-keychain
|
|
run: |
|
|
# Debug log helper
|
|
function debug_log() {
|
|
echo "DEBUG: $1"
|
|
if [[ -n "$DEBUG_LOG_PATH" ]]; then
|
|
echo "$(date "+%Y-%m-%d %H:%M:%S") - $1" >> "$DEBUG_LOG_PATH"
|
|
fi
|
|
}
|
|
|
|
debug_log "Setting up keychain"
|
|
|
|
# Create temporary keychain
|
|
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME"
|
|
security default-keychain -s "$KEYCHAIN_NAME"
|
|
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME"
|
|
security set-keychain-settings -t 3600 -u "$KEYCHAIN_NAME"
|
|
|
|
# Create certificate file
|
|
CERTIFICATE_PATH="$WORK_DIR/certificate.p12"
|
|
echo "${{ secrets.MACOS_CERTIFICATE }}" | base64 --decode > "$CERTIFICATE_PATH"
|
|
|
|
# Add to keychain
|
|
debug_log "Importing certificate into keychain"
|
|
security import "$CERTIFICATE_PATH" -k "$KEYCHAIN_NAME" -P "${{ secrets.MACOS_CERTIFICATE_PWD }}" -T /usr/bin/codesign
|
|
|
|
# Add to search list and set as default
|
|
security list-keychains -d user -s "$KEYCHAIN_NAME" login.keychain
|
|
security default-keychain -s "$KEYCHAIN_NAME"
|
|
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME"
|
|
|
|
# Allow codesign to access keychain items without prompting
|
|
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME"
|
|
|
|
# List all identities to find the exact name
|
|
debug_log "Listing all identities in the keychain:"
|
|
IDENTITY_INFO=$(security find-identity -v "$KEYCHAIN_NAME")
|
|
debug_log "$IDENTITY_INFO"
|
|
|
|
# Parse the exact identity name from the output
|
|
EXACT_IDENTITY=$(echo "$IDENTITY_INFO" | grep "Developer ID Application" | head -1 | sed -E 's/.*"(Developer ID Application: .*)"/\1/')
|
|
|
|
if [[ -n "$EXACT_IDENTITY" ]]; then
|
|
debug_log "Found exact identity: $EXACT_IDENTITY"
|
|
SIGNING_IDENTITY="$EXACT_IDENTITY"
|
|
echo "SIGNING_IDENTITY=$SIGNING_IDENTITY" >> $GITHUB_ENV
|
|
echo "CERTIFICATE_AVAILABLE=true" >> $GITHUB_ENV
|
|
else
|
|
debug_log "WARNING: No Developer ID Application certificate found"
|
|
if [[ "false" == "true" ]]; then
|
|
debug_log "Falling back to ad-hoc signing"
|
|
echo "CERTIFICATE_AVAILABLE=adhoc" >> $GITHUB_ENV
|
|
else
|
|
debug_log "Not falling back to ad-hoc signing as specified"
|
|
echo "CERTIFICATE_AVAILABLE=false" >> $GITHUB_ENV
|
|
fi
|
|
fi
|
|
shell: bash
|
|
|
|
- name: Sign application
|
|
id: sign-app
|
|
run: |
|
|
# Debug log helper
|
|
function debug_log() {
|
|
echo "DEBUG: $1"
|
|
if [[ -n "$DEBUG_LOG_PATH" ]]; then
|
|
echo "$(date "+%Y-%m-%d %H:%M:%S") - $1" >> "$DEBUG_LOG_PATH"
|
|
fi
|
|
}
|
|
|
|
debug_log "Starting application signing process"
|
|
|
|
# Make sure keychain is unlocked and available
|
|
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME"
|
|
security list-keychains
|
|
security default-keychain
|
|
|
|
# Verify certificate exists
|
|
IDENTITY_INFO=$(security find-identity -v "$KEYCHAIN_NAME")
|
|
EXACT_IDENTITY=$(echo "$IDENTITY_INFO" | grep "Developer ID Application" | head -1 | sed -E 's/.*"(Developer ID Application: .*)"/\1/')
|
|
|
|
if [[ -z "$EXACT_IDENTITY" ]]; then
|
|
debug_log "ERROR: No Developer ID Application certificate found in keychain"
|
|
debug_log "$IDENTITY_INFO"
|
|
echo "SIGNING_RESULT=false" >> $GITHUB_ENV
|
|
exit 1
|
|
fi
|
|
|
|
debug_log "Found signing identity: $EXACT_IDENTITY"
|
|
SIGNING_IDENTITY="$EXACT_IDENTITY"
|
|
|
|
# Get hash ID if available for direct signing
|
|
if [[ "$IDENTITY_INFO" =~ ([0-9A-F]{40}) ]]; then
|
|
HASH_ID="${BASH_REMATCH[1]}"
|
|
debug_log "Using certificate hash: $HASH_ID"
|
|
else
|
|
HASH_ID=""
|
|
debug_log "No certificate hash found, using identity name"
|
|
fi
|
|
|
|
# Check entitlements file and validate it
|
|
ENTITLEMENTS_PATH="${{ env.ENTITLEMENTS_FILE }}"
|
|
USE_ENTITLEMENTS=false
|
|
|
|
if [[ -f "$ENTITLEMENTS_PATH" ]]; then
|
|
debug_log "Found entitlements file: $ENTITLEMENTS_PATH - validating..."
|
|
|
|
# Try to validate the entitlements file
|
|
plutil -lint "$ENTITLEMENTS_PATH" > /dev/null 2>&1
|
|
if [ $? -eq 0 ]; then
|
|
debug_log "Entitlements file is valid, will use for signing"
|
|
USE_ENTITLEMENTS=true
|
|
else
|
|
debug_log "WARNING: Entitlements file is invalid, will sign without entitlements"
|
|
# Print the entitlements file content for debugging
|
|
debug_log "Entitlements file content:"
|
|
cat "$ENTITLEMENTS_PATH" | tee -a "$DEBUG_LOG_PATH"
|
|
fi
|
|
else
|
|
debug_log "No entitlements file found at $ENTITLEMENTS_PATH, will sign without entitlements"
|
|
fi
|
|
|
|
# First remove existing signatures
|
|
debug_log "Removing existing signatures..."
|
|
codesign --remove-signature "$APP_PATH" || true
|
|
|
|
# Sign embedded components first
|
|
debug_log "Signing embedded components..."
|
|
|
|
# Sign MacOS directory contents
|
|
if [ -d "$APP_PATH/Contents/MacOS" ]; then
|
|
debug_log "Signing MacOS executables and libraries..."
|
|
# Sign dylibs first
|
|
if [ "$USE_ENTITLEMENTS" = true ]; then
|
|
find "$APP_PATH/Contents/MacOS" -type f -name "*.dylib" -exec codesign --force --timestamp --options runtime --entitlements "$ENTITLEMENTS_PATH" --sign "$SIGNING_IDENTITY" {} \; 2>/dev/null || true
|
|
else
|
|
find "$APP_PATH/Contents/MacOS" -type f -name "*.dylib" -exec codesign --force --timestamp --options runtime --sign "$SIGNING_IDENTITY" {} \; 2>/dev/null || true
|
|
fi
|
|
|
|
# Sign all executables
|
|
if [ "$USE_ENTITLEMENTS" = true ]; then
|
|
find "$APP_PATH/Contents/MacOS" -type f -perm +111 -exec codesign --force --timestamp --options runtime --entitlements "$ENTITLEMENTS_PATH" --sign "$SIGNING_IDENTITY" {} \; 2>/dev/null || true
|
|
else
|
|
find "$APP_PATH/Contents/MacOS" -type f -perm +111 -exec codesign --force --timestamp --options runtime --sign "$SIGNING_IDENTITY" {} \; 2>/dev/null || true
|
|
fi
|
|
|
|
# Sign any remaining files
|
|
if [ "$USE_ENTITLEMENTS" = true ]; then
|
|
find "$APP_PATH/Contents/MacOS" -type f -not -name "*.dylib" -not -perm +111 -exec codesign --force --timestamp --options runtime --entitlements "$ENTITLEMENTS_PATH" --sign "$SIGNING_IDENTITY" {} \; 2>/dev/null || true
|
|
else
|
|
find "$APP_PATH/Contents/MacOS" -type f -not -name "*.dylib" -not -perm +111 -exec codesign --force --timestamp --options runtime --sign "$SIGNING_IDENTITY" {} \; 2>/dev/null || true
|
|
fi
|
|
else
|
|
debug_log "No MacOS directory found"
|
|
fi
|
|
|
|
# Sign Frameworks if they exist
|
|
if [ -d "$APP_PATH/Contents/Frameworks" ]; then
|
|
debug_log "Signing Frameworks directory..."
|
|
# Sign individual files in Frameworks
|
|
if [ "$USE_ENTITLEMENTS" = true ]; then
|
|
find "$APP_PATH/Contents/Frameworks" -type f -not -path "*.framework/*" -exec codesign --force --timestamp --options runtime --entitlements "$ENTITLEMENTS_PATH" --sign "$SIGNING_IDENTITY" {} \; 2>/dev/null || true
|
|
# Sign framework bundles
|
|
find "$APP_PATH/Contents/Frameworks" -type d -name "*.framework" -exec codesign --force --timestamp --options runtime --entitlements "$ENTITLEMENTS_PATH" --sign "$SIGNING_IDENTITY" {} \; 2>/dev/null || true
|
|
else
|
|
find "$APP_PATH/Contents/Frameworks" -type f -not -path "*.framework/*" -exec codesign --force --timestamp --options runtime --sign "$SIGNING_IDENTITY" {} \; 2>/dev/null || true
|
|
# Sign framework bundles
|
|
find "$APP_PATH/Contents/Frameworks" -type d -name "*.framework" -exec codesign --force --timestamp --options runtime --sign "$SIGNING_IDENTITY" {} \; 2>/dev/null || true
|
|
fi
|
|
else
|
|
debug_log "No Frameworks directory found"
|
|
fi
|
|
|
|
# Sign any plugins if they exist
|
|
if [ -d "$APP_PATH/Contents/PlugIns" ]; then
|
|
debug_log "Signing PlugIns directory..."
|
|
if [ "$USE_ENTITLEMENTS" = true ]; then
|
|
find "$APP_PATH/Contents/PlugIns" -type d -name "*.appex" -exec codesign --force --timestamp --options runtime --entitlements "$ENTITLEMENTS_PATH" --sign "$SIGNING_IDENTITY" {} \; 2>/dev/null || true
|
|
else
|
|
find "$APP_PATH/Contents/PlugIns" -type d -name "*.appex" -exec codesign --force --timestamp --options runtime --sign "$SIGNING_IDENTITY" {} \; 2>/dev/null || true
|
|
fi
|
|
fi
|
|
|
|
# Sign main app bundle (use hash ID if available, otherwise use identity name)
|
|
debug_log "Signing main app bundle..."
|
|
if [ "$USE_ENTITLEMENTS" = true ]; then
|
|
debug_log "Using entitlements file: $ENTITLEMENTS_PATH"
|
|
if [[ -n "$HASH_ID" ]]; then
|
|
codesign --force --deep --timestamp --options runtime --entitlements "$ENTITLEMENTS_PATH" --sign "$HASH_ID" "$APP_PATH"
|
|
else
|
|
codesign --force --deep --timestamp --options runtime --entitlements "$ENTITLEMENTS_PATH" --sign "$SIGNING_IDENTITY" "$APP_PATH"
|
|
fi
|
|
else
|
|
debug_log "Signing without entitlements"
|
|
if [[ -n "$HASH_ID" ]]; then
|
|
codesign --force --deep --timestamp --options runtime --sign "$HASH_ID" "$APP_PATH"
|
|
else
|
|
codesign --force --deep --timestamp --options runtime --sign "$SIGNING_IDENTITY" "$APP_PATH"
|
|
fi
|
|
fi
|
|
|
|
SIGN_RESULT=$?
|
|
if [ $SIGN_RESULT -eq 0 ]; then
|
|
debug_log "App signed successfully"
|
|
echo "SIGNING_RESULT=true" >> $GITHUB_ENV
|
|
|
|
# Verify signature
|
|
debug_log "Verifying app signature..."
|
|
codesign -dvv "$APP_PATH"
|
|
VERIFY_RESULT=$?
|
|
|
|
if [ $VERIFY_RESULT -eq 0 ]; then
|
|
debug_log "Signature verification successful"
|
|
else
|
|
debug_log "WARNING: Signature verification failed, app may not be properly signed"
|
|
# Continue anyway since the signing appeared to succeed
|
|
fi
|
|
else
|
|
debug_log "ERROR: App signing failed with exit code: $SIGN_RESULT"
|
|
echo "SIGNING_RESULT=false" >> $GITHUB_ENV
|
|
exit 1
|
|
fi
|
|
shell: bash
|
|
|
|
- name: Notarize application
|
|
id: notarize-app
|
|
if: env.SIGNING_RESULT == 'true'
|
|
run: |
|
|
# Debug log helper
|
|
function debug_log() {
|
|
echo "DEBUG: $1"
|
|
if [[ -n "$DEBUG_LOG_PATH" ]]; then
|
|
echo "$(date "+%Y-%m-%d %H:%M:%S") - $1" >> "$DEBUG_LOG_PATH"
|
|
fi
|
|
}
|
|
|
|
debug_log "Starting notarization process"
|
|
|
|
# Create ZIP for notarization
|
|
debug_log "Creating ZIP archive for notarization"
|
|
ditto -c -k --keepParent "$APP_PATH" "$ZIP_PATH"
|
|
if [ $? -ne 0 ]; then
|
|
debug_log "Error creating ZIP archive"
|
|
echo "NOTARIZATION_RESULT=false" >> $GITHUB_ENV
|
|
exit 1
|
|
fi
|
|
|
|
debug_log "ZIP archive created successfully at: $ZIP_PATH"
|
|
ZIP_SIZE=$(du -h "$ZIP_PATH" | cut -f1)
|
|
debug_log "ZIP archive size: $ZIP_SIZE"
|
|
|
|
# Submit for notarization - use separate submission and polling
|
|
debug_log "Submitting app for notarization..."
|
|
|
|
# Create submission file command from parts to avoid shell re-interpretation
|
|
SUBMIT_CMD="xcrun notarytool submit \"$ZIP_PATH\" --key \"$API_KEY_FILE\" --key-id \"${{ secrets.NOTARY_API_KEY_ID }}\" --issuer \"${{ secrets.NOTARY_API_KEY_ISSUER_ID }}\""
|
|
debug_log "Running command: $SUBMIT_CMD"
|
|
|
|
# Submit with error capture
|
|
SUBMIT_OUTPUT=$(eval "$SUBMIT_CMD" 2>&1)
|
|
SUBMIT_STATUS=$?
|
|
|
|
# Save output for detailed analysis
|
|
echo "$SUBMIT_OUTPUT" > "$WORK_DIR/submit_output.txt"
|
|
cat "$WORK_DIR/submit_output.txt" | tee -a "$DEBUG_LOG_PATH"
|
|
|
|
if [ $SUBMIT_STATUS -ne 0 ]; then
|
|
debug_log "ERROR: Failed to submit for notarization, exit code: $SUBMIT_STATUS"
|
|
echo "NOTARIZATION_RESULT=false" >> $GITHUB_ENV
|
|
exit 1
|
|
fi
|
|
|
|
# Extract the request UUID (handle different output formats)
|
|
REQUEST_UUID=$(echo "$SUBMIT_OUTPUT" | grep -o "id: [a-z0-9-]*" | cut -d' ' -f2)
|
|
|
|
if [ -z "$REQUEST_UUID" ]; then
|
|
# Alternative grep pattern
|
|
REQUEST_UUID=$(echo "$SUBMIT_OUTPUT" | grep -o "[a-f0-9]\{8\}-[a-f0-9]\{4\}-[a-f0-9]\{4\}-[a-f0-9]\{4\}-[a-f0-9]\{12\}")
|
|
fi
|
|
|
|
if [ -z "$REQUEST_UUID" ]; then
|
|
debug_log "ERROR: Failed to extract UUID from notarization submission"
|
|
debug_log "Full submission output:"
|
|
cat "$WORK_DIR/submit_output.txt"
|
|
echo "NOTARIZATION_RESULT=false" >> $GITHUB_ENV
|
|
exit 1
|
|
fi
|
|
|
|
debug_log "Notarization submission UUID: $REQUEST_UUID"
|
|
echo "NOTARIZATION_UUID=$REQUEST_UUID" >> $GITHUB_ENV
|
|
|
|
# Wait for notarization to complete with polling
|
|
debug_log "Waiting for notarization to complete (this may take several minutes)..."
|
|
|
|
WAIT_COUNTER=1
|
|
TOTAL_WAIT=60 # 60 minutes maximum
|
|
|
|
while [ $WAIT_COUNTER -le $TOTAL_WAIT ]; do
|
|
if [ $WAIT_COUNTER -gt 1 ]; then
|
|
debug_log "Waiting 60 seconds before checking again (attempt $WAIT_COUNTER/$TOTAL_WAIT)..."
|
|
sleep 60
|
|
fi
|
|
|
|
# Check status
|
|
STATUS_CMD="xcrun notarytool info \"$REQUEST_UUID\" --key \"$API_KEY_FILE\" --key-id \"${{ secrets.NOTARY_API_KEY_ID }}\" --issuer \"${{ secrets.NOTARY_API_KEY_ISSUER_ID }}\""
|
|
debug_log "Checking status with: $STATUS_CMD"
|
|
STATUS_OUTPUT=$(eval "$STATUS_CMD" 2>&1)
|
|
STATUS_CODE=$?
|
|
|
|
echo "$STATUS_OUTPUT" > "$WORK_DIR/status_output_$WAIT_COUNTER.txt"
|
|
cat "$WORK_DIR/status_output_$WAIT_COUNTER.txt" | tee -a "$DEBUG_LOG_PATH"
|
|
|
|
if [ $STATUS_CODE -ne 0 ]; then
|
|
debug_log "WARNING: Status check failed, exit code: $STATUS_CODE"
|
|
# Continue anyway to retry
|
|
else
|
|
# Extract status
|
|
REQUEST_STATUS=$(echo "$STATUS_OUTPUT" | grep -o "status: [A-Za-z]*" | cut -d' ' -f2)
|
|
|
|
if [ -z "$REQUEST_STATUS" ]; then
|
|
debug_log "WARNING: Could not extract status from output"
|
|
else
|
|
debug_log "Notarization status: $REQUEST_STATUS"
|
|
|
|
if [ "$REQUEST_STATUS" = "Accepted" ]; then
|
|
debug_log "Notarization successful!"
|
|
echo "NOTARIZATION_RESULT=true" >> $GITHUB_ENV
|
|
break
|
|
elif [ "$REQUEST_STATUS" = "Invalid" ] || [ "$REQUEST_STATUS" = "Rejected" ]; then
|
|
debug_log "ERROR: Notarization failed with status: $REQUEST_STATUS"
|
|
echo "NOTARIZATION_RESULT=false" >> $GITHUB_ENV
|
|
exit 1
|
|
fi
|
|
# In progress - continue waiting
|
|
fi
|
|
fi
|
|
|
|
WAIT_COUNTER=$((WAIT_COUNTER+1))
|
|
done
|
|
|
|
# Check final status
|
|
if [ $WAIT_COUNTER -gt $TOTAL_WAIT ]; then
|
|
debug_log "ERROR: Notarization wait timeout after $TOTAL_WAIT minutes"
|
|
echo "NOTARIZATION_RESULT=false" >> $GITHUB_ENV
|
|
exit 1
|
|
fi
|
|
|
|
if [ "$REQUEST_STATUS" != "Accepted" ]; then
|
|
debug_log "ERROR: Notarization failed or timed out"
|
|
echo "NOTARIZATION_RESULT=false" >> $GITHUB_ENV
|
|
exit 1
|
|
fi
|
|
|
|
# Success - now staple the ticket
|
|
debug_log "Notarization complete, proceeding to staple the ticket"
|
|
xcrun stapler staple "$APP_PATH"
|
|
STAPLE_RESULT=$?
|
|
|
|
if [ $STAPLE_RESULT -eq 0 ]; then
|
|
debug_log "Notarization ticket stapled successfully"
|
|
echo "STAPLING_RESULT=true" >> $GITHUB_ENV
|
|
|
|
# Verify stapling
|
|
xcrun stapler validate "$APP_PATH"
|
|
VALIDATE_RESULT=$?
|
|
|
|
if [ $VALIDATE_RESULT -eq 0 ]; then
|
|
debug_log "Stapling validation successful"
|
|
echo "VERIFY_RESULT=true" >> $GITHUB_ENV
|
|
else
|
|
debug_log "WARNING: Stapling validation failed but continuing"
|
|
echo "VERIFY_RESULT=false" >> $GITHUB_ENV
|
|
fi
|
|
else
|
|
debug_log "ERROR: Failed to staple notarization ticket"
|
|
echo "STAPLING_RESULT=false" >> $GITHUB_ENV
|
|
exit 1
|
|
fi
|
|
shell: bash
|
|
|
|
- name: Staple notarization ticket
|
|
id: staple-ticket
|
|
if: env.SIGNING_RESULT == 'true' && env.NOTARIZATION_RESULT == 'true'
|
|
run: |
|
|
# Debug log helper
|
|
function debug_log() {
|
|
echo "DEBUG: $1"
|
|
if [[ -n "$DEBUG_LOG_PATH" ]]; then
|
|
echo "$(date "+%Y-%m-%d %H:%M:%S") - $1" >> "$DEBUG_LOG_PATH"
|
|
fi
|
|
}
|
|
|
|
debug_log "Stapling notarization ticket to app"
|
|
|
|
# Staple the ticket
|
|
xcrun stapler staple "$APP_PATH"
|
|
STAPLE_RESULT=$?
|
|
|
|
if [ $STAPLE_RESULT -eq 0 ]; then
|
|
debug_log "Notarization ticket stapled successfully"
|
|
echo "STAPLING_RESULT=true" >> $GITHUB_ENV
|
|
else
|
|
debug_log "ERROR: Stapling failed with exit code: $STAPLE_RESULT"
|
|
echo "STAPLING_RESULT=false" >> $GITHUB_ENV
|
|
exit 1
|
|
fi
|
|
shell: bash
|
|
|
|
- name: Verify notarization and stapling
|
|
id: verify-notarization
|
|
if: env.SIGNING_RESULT == 'true' && env.NOTARIZATION_RESULT == 'true' && env.STAPLING_RESULT == 'true'
|
|
run: |
|
|
# Debug log helper
|
|
function debug_log() {
|
|
echo "DEBUG: $1"
|
|
if [[ -n "$DEBUG_LOG_PATH" ]]; then
|
|
echo "$(date "+%Y-%m-%d %H:%M:%S") - $1" >> "$DEBUG_LOG_PATH"
|
|
fi
|
|
}
|
|
|
|
debug_log "Verifying notarization and stapling"
|
|
|
|
# Verify code signature with staple
|
|
debug_log "Verifying code signature..."
|
|
codesign --verify --deep --strict --verbose=2 "$APP_PATH"
|
|
VERIFY_RESULT=$?
|
|
|
|
if [ $VERIFY_RESULT -eq 0 ]; then
|
|
debug_log "Code signature verification successful"
|
|
else
|
|
debug_log "ERROR: Code signature verification failed with exit code: $VERIFY_RESULT"
|
|
exit 1
|
|
fi
|
|
|
|
# Verify stapling
|
|
debug_log "Verifying stapling..."
|
|
xcrun stapler validate "$APP_PATH"
|
|
STAPLE_VERIFY_RESULT=$?
|
|
|
|
if [ $STAPLE_VERIFY_RESULT -eq 0 ]; then
|
|
debug_log "Stapling verification successful"
|
|
echo "VERIFY_RESULT=true" >> $GITHUB_ENV
|
|
else
|
|
debug_log "ERROR: Stapling verification failed with exit code: $STAPLE_VERIFY_RESULT"
|
|
echo "VERIFY_RESULT=false" >> $GITHUB_ENV
|
|
exit 1
|
|
fi
|
|
shell: bash
|
|
|
|
- name: Remove quarantine attribute
|
|
id: remove-quarantine
|
|
if: env.SIGNING_RESULT == 'true' && env.NOTARIZATION_RESULT == 'true' && env.STAPLING_RESULT == 'true'
|
|
run: |
|
|
# Debug log helper
|
|
function debug_log() {
|
|
echo "DEBUG: $1"
|
|
if [[ -n "$DEBUG_LOG_PATH" ]]; then
|
|
echo "$(date "+%Y-%m-%d %H:%M:%S") - $1" >> "$DEBUG_LOG_PATH"
|
|
fi
|
|
}
|
|
|
|
debug_log "Removing quarantine attribute from app"
|
|
|
|
# Directly run the command without creating a script file
|
|
debug_log "Removing quarantine attribute from all files..."
|
|
find "$APP_PATH" -exec xattr -d com.apple.quarantine {} \; 2>/dev/null || true
|
|
debug_log "Quarantine attributes removed"
|
|
shell: bash
|
|
|
|
- name: Package signed app
|
|
id: package-app
|
|
if: env.SIGNING_RESULT == 'true' && env.NOTARIZATION_RESULT == 'true' && env.STAPLING_RESULT == 'true'
|
|
run: |
|
|
# Debug log helper
|
|
function debug_log() {
|
|
echo "DEBUG: $1"
|
|
if [[ -n "$DEBUG_LOG_PATH" ]]; then
|
|
echo "$(date "+%Y-%m-%d %H:%M:%S") - $1" >> "$DEBUG_LOG_PATH"
|
|
fi
|
|
}
|
|
|
|
debug_log "Packaging the signed and notarized app"
|
|
|
|
# Create DMG using hdiutil
|
|
debug_log "Creating DMG package..."
|
|
hdiutil create -volname "$APP_NAME" -srcfolder "$APP_PATH" -ov -format UDZO "$DMG_PATH"
|
|
DMG_CREATE_RESULT=$?
|
|
|
|
# Check DMG creation result
|
|
if [ $DMG_CREATE_RESULT -eq 0 ]; then
|
|
debug_log "DMG package created successfully at: $DMG_PATH"
|
|
DMG_SIZE=$(du -h "$DMG_PATH" | cut -f1)
|
|
debug_log "DMG size: $DMG_SIZE"
|
|
echo "DMG_CREATED=true" >> $GITHUB_ENV
|
|
echo "PACKAGE_PATH=$DMG_PATH" >> $GITHUB_ENV
|
|
else
|
|
debug_log "WARNING: DMG creation failed, using ZIP as distribution package"
|
|
echo "DMG_CREATED=false" >> $GITHUB_ENV
|
|
echo "PACKAGE_PATH=$ZIP_PATH" >> $GITHUB_ENV
|
|
fi
|
|
shell: bash
|
|
|
|
- name: Clean up
|
|
id: cleanup
|
|
if: always()
|
|
run: |
|
|
# Debug log helper
|
|
function debug_log() {
|
|
echo "DEBUG: $1"
|
|
if [[ -n "$DEBUG_LOG_PATH" ]]; then
|
|
echo "$(date "+%Y-%m-%d %H:%M:%S") - $1" >> "$DEBUG_LOG_PATH"
|
|
fi
|
|
}
|
|
|
|
debug_log "Cleaning up resources"
|
|
|
|
# Save state message for better debugging
|
|
if [[ "$NOTARIZATION_RESULT" == "true" ]]; then
|
|
debug_log "Cleanup after successful notarization"
|
|
else
|
|
debug_log "Cleanup after notarization state: $NOTARIZATION_RESULT"
|
|
fi
|
|
|
|
# Clean up keychain
|
|
if [[ -n "$KEYCHAIN_NAME" ]]; then
|
|
security delete-keychain "$KEYCHAIN_NAME" || true
|
|
debug_log "Keychain deleted"
|
|
fi
|
|
|
|
# Clean up temporary files
|
|
if [[ -d "$WORK_DIR" ]]; then
|
|
# Save notarization logs first
|
|
if [[ -d "$DEBUG_LOG_PATH" ]] && [[ -d "$WORK_DIR" ]]; then
|
|
debug_log "Saving work directory files to debug logs"
|
|
cp -R "$WORK_DIR/"*.txt "$DEBUG_LOG_PATH/" 2>/dev/null || true
|
|
fi
|
|
|
|
rm -rf "$WORK_DIR" || true
|
|
debug_log "Temporary files deleted"
|
|
fi
|
|
|
|
debug_log "Cleanup completed"
|
|
shell: bash
|
|
|
|
# Upload debug logs if available
|
|
- name: Upload Debug Logs
|
|
uses: actions/upload-artifact@v3
|
|
if: always()
|
|
with:
|
|
name: notarize-debug-logs
|
|
path: debug_logs
|
|
retention-days: 7
|
|
|
|
# Upload only the DMG file as main distribution artifact
|
|
- name: Upload Mac Distribution DMG
|
|
uses: actions/upload-artifact@v3
|
|
if: env.NOTARIZED_STATUS == 'true' && env.SIGNED_STATUS != 'none'
|
|
with:
|
|
name: LuckyWorld-Mac-Distribution
|
|
path: ${{ env.PACKAGE_PATH }}
|
|
retention-days: 30
|
|
|
|
# Report results
|
|
- name: Report Results
|
|
run: |
|
|
echo "🔐 App signing: ${{ env.SIGNED_STATUS }}"
|
|
echo "🔏 App notarization: ${{ env.NOTARIZED_STATUS }}"
|
|
|
|
if [ "${{ env.SIGNED_STATUS }}" != "none" ]; then
|
|
echo "✅ Packaging completed successfully!"
|
|
echo "Final package: ${{ env.PACKAGE_PATH }}"
|
|
else
|
|
echo "⚠️ App was not signed - check the logs for details"
|
|
fi
|
|
shell: bash
|
|
|