-
Notifications
You must be signed in to change notification settings - Fork 47
Fix Google Play signed APK download with smart checking #101
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Remove job dependencies to test download independently - Hardcode v2.3.3 version values for testing existing APK - This is a temporary test configuration 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
- Remove AAB artifact download step (not needed) - Remove fallback bundletool generation (focus only on signed APK) - Simplify workflow to only download actual signed APK from Google Play 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
- Use generatedapks.list to get downloadId from APK manifest
- Use generatedapks.download to fetch binary APK with downloadId
- Improve universal APK detection with better targeting logic
- Add detailed logging for debugging APK selection
Based on correct API documentation:
- GET /applications/{packageName}/generatedApks/{versionCode}
- GET /applications/{packageName}/generatedApks/{versionCode}/downloads/{downloadId}:download
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <[email protected]>
- Print complete APK object structure to understand available fields - This will help identify the correct field name for downloadId 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
…cture - Changed from looking for generatedUniversalApK field to generatedSplitApks array - Look for base module with variantId=1 and no splitId to identify universal APK - Added better debugging output to understand API response structure - Fixed duplicate download_id validation
- Replace direct execute() call with proper Google API media download - Add progress tracking during download - Use BytesIO buffer for binary data handling
- Remove hard-coded variantId=1 requirement - Accept any variant ID for base module without splitId - Add better debugging output to show splitId values
- Try variantId=1 without splitId first (original approach) - Fall back to variantId=2 without splitId - Fall back to variantId=3 without splitId - Finally try first base module even with splitId - Add detailed logging for each strategy attempt
- Try variantId=2 first since variantId=1 consistently returns 404 - Use variantId=3 as secondary option - Keep variantId=1 as last resort for debugging - Add better logging for all base APK candidates
- Changed from download() to download_media() method - This fixes the HTTP 204 error when downloading signed APKs - download_media() returns the actual media content instead of metadata 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
- First check generatedUniversalApk field as per API documentation - Fallback to base module in generatedSplitApks if needed - Simplified variant selection logic - This should resolve HTTP 204 errors by using correct APK structure 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
- Add ?alt=media to download request URL to get binary content - Without alt=media, API returns HTTP 204 with metadata only - This follows Google APIs media download convention 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
- Check for existing query params and use & instead of ? - Prevents double question mark (??) in URL which caused HTTP 400 - Now properly handles URLs that already contain query parameters 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
- Add dependency on play-store-upload job - Add proper execution conditions (requires signing and play store upload enabled) - Use dynamic version info from prepare job instead of hardcoded values - Increase wait time to 5 minutes for Google Play processing - Update error messages to be more accurate - Ensure download job only runs after successful Play Store upload 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
- Check if Google Play signed APK already exists before waiting - Implement periodic polling (every 30s) with 10min maximum wait - Skip unnecessary waiting if APK is already available - Provide better progress feedback during waiting - Reduce overall pipeline execution time when APK is ready 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
- Use known existing version 2.3.3 (code 233) in Google Play - This should skip waiting and go directly to download - Will revert after testing to use dynamic version 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
- Remove job dependencies temporarily - Use hardcoded version 2.3.3 (233) which exists in Google Play - This should demonstrate immediate APK detection and skip waiting - Will revert after testing 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
This reverts commit 50a115b.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR fixes the Google Play signed APK download functionality by resolving HTTP 204 errors and implementing smart availability checking. The changes ensure proper binary content retrieval and reduce unnecessary waiting time during the CI/CD pipeline.
Key changes made:
- Fixed API calls by adding the
alt=mediaparameter to download binary content instead of metadata - Implemented smart checking logic to verify APK availability before waiting, with periodic polling
- Enhanced APK detection to prioritize universal APKs and fallback to base split APKs with proper targeting
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| download-signed-apk: | ||
| name: Download Google Play Signed APK | ||
| needs: [prepare, play-store-upload] | ||
| if: ${{ vars.ENABLE_PLAY_STORE_UPLOAD == 'true' && vars.ENABLE_SIGNING == 'true' }} |
Copilot
AI
Sep 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The condition expression has redundant ${{ }} wrapper. GitHub Actions automatically evaluates expressions in if conditions, so this should be if: vars.ENABLE_PLAY_STORE_UPLOAD == 'true' && vars.ENABLE_SIGNING == 'true'
| if: ${{ vars.ENABLE_PLAY_STORE_UPLOAD == 'true' && vars.ENABLE_SIGNING == 'true' }} | |
| if: vars.ENABLE_PLAY_STORE_UPLOAD == 'true' && vars.ENABLE_SIGNING == 'true' |
| # Add alt=media parameter correctly (URL already has query params, so use &) | ||
| if '?' in download_request.uri: | ||
| download_request.uri += '&alt=media' | ||
| else: | ||
| download_request.uri += '?alt=media' |
Copilot
AI
Sep 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Direct URI manipulation is fragile and error-prone. Consider using URL parsing libraries like urllib.parse to properly handle query parameters instead of string concatenation.
| # Add alt=media parameter correctly (URL already has query params, so use &) | |
| if '?' in download_request.uri: | |
| download_request.uri += '&alt=media' | |
| else: | |
| download_request.uri += '?alt=media' | |
| # Add alt=media parameter correctly using urllib.parse | |
| from urllib.parse import urlparse, parse_qs, urlencode, urlunparse | |
| parsed = urlparse(download_request.uri) | |
| query = parse_qs(parsed.query) | |
| query['alt'] = ['media'] | |
| new_query = urlencode(query, doseq=True) | |
| download_request.uri = urlunparse(parsed._replace(query=new_query)) |
| import io | ||
| from googleapiclient.http import MediaIoBaseDownload |
Copilot
AI
Sep 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Import statements should be placed at the top of the script with other imports (lines 414-419) rather than in the middle of the function for better code organization and readability.
- Universal APK is guaranteed to exist for all releases - Removed fallback logic that looked for base modules in split APKs - Simplified code by only checking for generatedUniversalApk field - This makes the code cleaner and more maintainable 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
Summary
alt=mediaparameter to download requestsKey Changes
alt=mediaparameter to get binary content instead of metadatageneratedUniversalApkfield first, then fallback to base split APKsTest Results
Fixes the
download-signed-apkjob that was failing with HTTP 204 errors.