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