@@ -386,8 +386,8 @@ jobs:
386386 download-signed-apk :
387387 name : Download Google Play Signed APK
388388 needs : [prepare, play-store-upload]
389+ if : ${{ vars.ENABLE_PLAY_STORE_UPLOAD == 'true' && vars.ENABLE_SIGNING == 'true' }}
389390 runs-on : ubuntu-latest
390- if : success()
391391
392392 steps :
393393 - name : Checkout code
@@ -402,17 +402,106 @@ jobs:
402402 run : |
403403 pip install google-api-python-client google-auth-httplib2 google-auth-oauthlib requests
404404
405- - name : Wait for Google Play processing
405+ - name : Check and wait for Google Play processing
406+ env :
407+ PLAY_STORE_SERVICE_ACCOUNT_JSON : ${{ secrets.PLAY_STORE_SERVICE_ACCOUNT_JSON }}
406408 run : |
407- echo "Waiting for Google Play to process and sign the APK... "
408- sleep 120 # Wait 2 minutes for Google Play to process
409+ VERSION_CODE="${{ needs.prepare.outputs.version_code }} "
410+ PACKAGE_NAME="me.ghui.v2er"
409411
410- - name : Download AAB artifact
411- uses : actions/download-artifact@v4
412- with :
413- name : release-bundle
414- path : bundle-artifacts/
415- continue-on-error : true
412+ # Create Python script to check if APK exists
413+ cat > check_apk_exists.py << 'EOF'
414+ import json
415+ import os
416+ import sys
417+ import time
418+ from google.oauth2 import service_account
419+ from googleapiclient.discovery import build
420+
421+ def check_apk_exists():
422+ try:
423+ # Load service account credentials
424+ service_account_info = json.loads(os.environ['PLAY_STORE_SERVICE_ACCOUNT_JSON'])
425+ credentials = service_account.Credentials.from_service_account_info(
426+ service_account_info,
427+ scopes=['https://www.googleapis.com/auth/androidpublisher']
428+ )
429+
430+ # Build the service
431+ service = build('androidpublisher', 'v3', credentials=credentials)
432+
433+ package_name = os.environ['PACKAGE_NAME']
434+ version_code = int(os.environ['VERSION_CODE'])
435+
436+ print(f"Checking if signed APK exists for {package_name} version {version_code}")
437+
438+ # Try to get the generated APKs list
439+ result = service.generatedapks().list(
440+ packageName=package_name,
441+ versionCode=version_code
442+ ).execute()
443+
444+ if 'generatedApks' not in result or not result['generatedApks']:
445+ print(f"No generated APKs found for version {version_code}")
446+ return False
447+
448+ print(f"Found {len(result['generatedApks'])} generated APK groups")
449+
450+ # Check if we can find a universal APK
451+ for apk in result['generatedApks']:
452+ if 'generatedUniversalApk' in apk:
453+ universal_apk = apk['generatedUniversalApk']
454+ download_id = universal_apk.get('downloadId')
455+ if download_id:
456+ print(f"✅ Universal APK found with downloadId: {download_id}")
457+ return True
458+
459+ print("❌ No universal APK found")
460+ return False
461+
462+ except Exception as e:
463+ print(f"Error checking APK: {str(e)}")
464+ return False
465+
466+ if __name__ == "__main__":
467+ exists = check_apk_exists()
468+ sys.exit(0 if exists else 1)
469+ EOF
470+
471+ # Set environment variables for the script
472+ export PACKAGE_NAME="$PACKAGE_NAME"
473+ export VERSION_CODE="$VERSION_CODE"
474+
475+ # Check if APK already exists
476+ echo "Checking if Google Play signed APK is ready..."
477+ if python3 check_apk_exists.py; then
478+ echo "✅ APK is already available, skipping wait"
479+ else
480+ echo "⏳ APK not ready yet, waiting for Google Play to process..."
481+
482+ # Smart waiting with periodic checks
483+ MAX_WAIT=600 # Maximum 10 minutes
484+ CHECK_INTERVAL=30 # Check every 30 seconds
485+ elapsed=0
486+
487+ while [ $elapsed -lt $MAX_WAIT ]; do
488+ sleep $CHECK_INTERVAL
489+ elapsed=$((elapsed + CHECK_INTERVAL))
490+
491+ echo "⏱️ Waited ${elapsed}s, checking again..."
492+ if python3 check_apk_exists.py; then
493+ echo "✅ APK is now available after ${elapsed}s"
494+ break
495+ fi
496+
497+ if [ $elapsed -ge $MAX_WAIT ]; then
498+ echo "⚠️ Maximum wait time (${MAX_WAIT}s) reached"
499+ echo "APK may still be processing, will attempt download anyway"
500+ fi
501+ done
502+ fi
503+
504+ # AAB artifact not needed for Google Play signed APK download
416505
417506 - name : Download Google Play Signed APK
418507 id : download-apk
@@ -449,8 +538,8 @@ jobs:
449538
450539 print(f"Attempting to download signed APK for {package_name} version {version_code}")
451540
452- # Get the signed universal APK download URL
453- # Note: This requires the app to be released and processed by Google Play
541+ # Step 1: Get the generated APKs list to find downloadId
542+ print("Getting generated APKs list...")
454543 result = service.generatedapks().list(
455544 packageName=package_name,
456545 versionCode=version_code
@@ -460,32 +549,68 @@ jobs:
460549 print("No generated APKs found. App may not be processed yet by Google Play.")
461550 return False
462551
463- # Find universal APK
552+ print(f"Found {len(result['generatedApks'])} generated APKs")
553+
554+ # Debug: Print all APK structures
555+ for i, apk in enumerate(result['generatedApks']):
556+ print(f"APK {i} structure:")
557+ for key, value in apk.items():
558+ print(f" {key}: {value}")
559+ print()
560+
561+ # Find universal APK using the correct API structure
562+ download_id = None
464563 universal_apk = None
564+
565+ # First, try to find a universal APK in generatedUniversalApk
465566 for apk in result['generatedApks']:
466- if apk.get('targetingInfo', {}).get('abiTargeting') is None:
467- # This should be the universal APK
468- universal_apk = apk
567+ if 'generatedUniversalApk' in apk:
568+ universal_apk = apk['generatedUniversalApk']
569+ download_id = universal_apk.get('downloadId')
570+ print(f"Found universal APK: {universal_apk}")
469571 break
470572
471- if not universal_apk:
472- print("Universal APK not found in generated APKs")
573+ if not download_id:
574+ print("No universal APK found")
575+ print("Available APK structure:")
576+ print(json.dumps(result['generatedApks'], indent=2))
473577 return False
474578
475- # Download the APK
476- download_url = universal_apk.get('downloadUrl')
477- if not download_url:
478- print("Download URL not available for universal APK")
479- return False
579+ print(f"Found universal APK with downloadId: {download_id}")
480580
481- print(f"Downloading APK from: {download_url}")
482- response = requests.get(download_url, stream=True)
483- response.raise_for_status()
581+ # Step 2: Download the APK using the downloadId
582+ print("Downloading APK binary...")
583+
584+ # Use alt=media to get the actual binary content instead of metadata
585+ download_request = service.generatedapks().download(
586+ packageName=package_name,
587+ versionCode=version_code,
588+ downloadId=download_id
589+ )
590+ # Add alt=media parameter correctly (URL already has query params, so use &)
591+ if '?' in download_request.uri:
592+ download_request.uri += '&alt=media'
593+ else:
594+ download_request.uri += '?alt=media'
484595
485596 output_filename = f"v2er-{os.environ['VERSION_NAME']}_google_play_signed.apk"
597+
598+ # Use media download with googleapiclient.http to handle binary content
599+ import io
600+ from googleapiclient.http import MediaIoBaseDownload
601+
602+ file_io = io.BytesIO()
603+ downloader = MediaIoBaseDownload(file_io, download_request)
604+
605+ done = False
606+ while done is False:
607+ status, done = downloader.next_chunk()
608+ if status:
609+ print(f"Download progress: {int(status.progress() * 100)}%")
610+
611+ # Write to file
486612 with open(output_filename, 'wb') as f:
487- for chunk in response.iter_content(chunk_size=8192):
488- f.write(chunk)
613+ f.write(file_io.getvalue())
489614
490615 print(f"Successfully downloaded: {output_filename}")
491616 print(f"apk_path={output_filename}")
@@ -526,46 +651,9 @@ jobs:
526651 echo "found=false" >> $GITHUB_OUTPUT
527652 fi
528653 else
529- echo "Failed to download Google Play signed APK, falling back to universal APK generation "
654+ echo "Failed to download Google Play signed APK"
530655 cat download_output.txt
531-
532- # Fallback: Generate universal APK from AAB as before
533- AAB_PATH=$(find bundle-artifacts -name "*.aab" 2>/dev/null | head -1)
534- if [ -z "$AAB_PATH" ]; then
535- echo "No AAB found for fallback, skipping"
536- echo "found=false" >> $GITHUB_OUTPUT
537- exit 0
538- fi
539-
540- echo "Generating universal APK from AAB as fallback..."
541-
542- # Download bundletool
543- curl -L -o bundletool-all.jar https://github.com/google/bundletool/releases/latest/download/bundletool-all.jar
544-
545- # Create dummy keystore
546- keytool -genkey -v -keystore debug.keystore -alias androiddebugkey \
547- -keyalg RSA -keysize 2048 -validity 10000 \
548- -dname "CN=Android Debug,O=Android,C=US" \
549- -storepass android -keypass android
550-
551- # Generate universal APK
552- java -jar bundletool-all.jar build-apks \
553- --bundle="$AAB_PATH" \
554- --output=apks.apks \
555- --mode=universal \
556- --ks=debug.keystore \
557- --ks-pass=pass:android \
558- --ks-key-alias=androiddebugkey \
559- --key-pass=pass:android
560-
561- # Extract APK
562- unzip -q apks.apks universal.apk
563- OUTPUT_FILE="v2er-${VERSION_NAME}_google_play_signed.apk"
564- mv universal.apk "$OUTPUT_FILE"
565-
566- echo "Generated fallback APK: $OUTPUT_FILE"
567- echo "apk_path=$OUTPUT_FILE" >> $GITHUB_OUTPUT
568- echo "found=true" >> $GITHUB_OUTPUT
656+ echo "found=false" >> $GITHUB_OUTPUT
569657 fi
570658
571659 - name : Create Google Play link info
@@ -625,6 +713,10 @@ jobs:
625713 echo "- When installed from Play Store, it will use Google Play's signing certificate" >> $GITHUB_STEP_SUMMARY
626714 echo "- The APK has been uploaded to the GitHub Release" >> $GITHUB_STEP_SUMMARY
627715 else
628- echo "⚠️ **No AAB found in artifacts**" >> $GITHUB_STEP_SUMMARY
629- echo "Signed APK generation requires a release bundle (AAB)" >> $GITHUB_STEP_SUMMARY
716+ echo "⚠️ **Google Play signed APK download failed**" >> $GITHUB_STEP_SUMMARY
717+ echo "" >> $GITHUB_STEP_SUMMARY
718+ echo "This may be because:" >> $GITHUB_STEP_SUMMARY
719+ echo "- Google Play is still processing the upload" >> $GITHUB_STEP_SUMMARY
720+ echo "- The version hasn't been released to any track yet" >> $GITHUB_STEP_SUMMARY
721+ echo "- API permissions are insufficient" >> $GITHUB_STEP_SUMMARY
630722 fi
0 commit comments