Fix Python syntax error: use sys.exit() instead of exit #15
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release APK | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| release_name: | |
| description: Override release name (defaults to tag or ref name). | |
| required: false | |
| play_track: | |
| description: Google Play release track (internal, alpha, beta, production). | |
| required: false | |
| default: internal | |
| type: choice | |
| options: | |
| - internal | |
| - alpha | |
| - beta | |
| - production | |
| push: | |
| branches: | |
| - main | |
| - ci-cd-setup | |
| permissions: | |
| contents: write | |
| env: | |
| BUILD_TOOLS_VERSION: 35.0.0 | |
| RELEASE_BUNDLE_PATH: app/build/outputs/bundle/release/app-release.aab | |
| jobs: | |
| release: | |
| name: Build and Publish | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Verify ref on main (workflow_dispatch) | |
| if: github.event_name == 'workflow_dispatch' | |
| run: | | |
| if [ "${GITHUB_REF}" != "refs/heads/main" ]; then | |
| echo "Workflow must target the main branch. Ref: ${GITHUB_REF}" >&2 | |
| exit 1 | |
| fi | |
| - name: Set up Java | |
| uses: actions/setup-java@v4 | |
| with: | |
| distribution: temurin | |
| java-version: 17 | |
| cache: gradle | |
| - name: Set up Android SDK | |
| uses: android-actions/setup-android@v3 | |
| - name: Accept Android SDK licenses and install packages | |
| run: | | |
| # Find sdkmanager in common locations | |
| if [ -f "$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" ]; then | |
| SDK_MANAGER="$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" | |
| elif [ -f "$ANDROID_HOME/cmdline-tools/bin/sdkmanager" ]; then | |
| SDK_MANAGER="$ANDROID_HOME/cmdline-tools/bin/sdkmanager" | |
| elif [ -f "$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager" ]; then | |
| SDK_MANAGER="$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager" | |
| else | |
| echo "Error: sdkmanager not found" | |
| exit 1 | |
| fi | |
| echo "Using sdkmanager at: $SDK_MANAGER" | |
| # Accept all licenses first | |
| yes | "$SDK_MANAGER" --licenses | |
| # Install packages separately (as per Stack Overflow solution) | |
| # Install platform-tools first | |
| yes | "$SDK_MANAGER" "platform-tools" | |
| # Install Android API 35 | |
| yes | "$SDK_MANAGER" "platforms;android-35" | |
| # Install build-tools | |
| yes | "$SDK_MANAGER" "build-tools;${{ env.BUILD_TOOLS_VERSION }}" | |
| # Install Android API 36 | |
| yes | "$SDK_MANAGER" "platforms;android-36" | |
| - name: Cache Gradle | |
| uses: gradle/actions/setup-gradle@v4 | |
| - name: Grant execute permission for Gradle wrapper | |
| run: chmod +x gradlew | |
| - name: Auto-increment version code from Google Play | |
| continue-on-error: true | |
| env: | |
| GOOGLE_PLAY_SERVICE_ACCOUNT_JSON: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON }} | |
| run: | | |
| if [ -z "$GOOGLE_PLAY_SERVICE_ACCOUNT_JSON" ]; then | |
| echo "Google Play service account not configured, skipping version code check" | |
| exit 0 | |
| fi | |
| # Write service account JSON to file | |
| echo "$GOOGLE_PLAY_SERVICE_ACCOUNT_JSON" > /tmp/service_account.json | |
| # Install Python dependencies | |
| python3 -m pip install --quiet google-api-python-client google-auth-httplib2 google-auth-oauthlib | |
| # Get latest version code from Google Play and update build.gradle.kts | |
| python3 << 'EOF' | |
| import json | |
| import os | |
| import re | |
| import sys | |
| import traceback | |
| from google.oauth2 import service_account | |
| from googleapiclient.discovery import build | |
| from googleapiclient.errors import HttpError | |
| PACKAGE_NAME = "llc.fungee.IngrediCheck" | |
| SERVICE_ACCOUNT_FILE = "/tmp/service_account.json" | |
| def get_latest_version_code_from_play(): | |
| """Query Google Play API to get the highest version code across all tracks.""" | |
| try: | |
| credentials = service_account.Credentials.from_service_account_file( | |
| SERVICE_ACCOUNT_FILE, | |
| scopes=['https://www.googleapis.com/auth/androidpublisher'] | |
| ) | |
| service = build('androidpublisher', 'v3', credentials=credentials) | |
| # Create a single edit to query all tracks | |
| edit = service.edits().insert(body={}, packageName=PACKAGE_NAME).execute() | |
| edit_id = edit['id'] | |
| max_version_code = 0 | |
| tracks = ['internal', 'alpha', 'beta', 'production'] | |
| # List all tracks at once (more efficient) | |
| try: | |
| tracks_list = service.edits().tracks().list( | |
| packageName=PACKAGE_NAME, | |
| editId=edit_id | |
| ).execute() | |
| if 'tracks' in tracks_list: | |
| for track in tracks_list['tracks']: | |
| if 'releases' in track: | |
| for release in track['releases']: | |
| if 'versionCodes' in release: | |
| for vc in release['versionCodes']: | |
| max_version_code = max(max_version_code, int(vc)) | |
| except HttpError as e: | |
| print(f"Error listing tracks: {e}") | |
| # Fallback: query each track individually | |
| for track_name in tracks: | |
| try: | |
| track_response = service.edits().tracks().get( | |
| packageName=PACKAGE_NAME, | |
| editId=edit_id, | |
| track=track_name | |
| ).execute() | |
| if 'releases' in track_response: | |
| for release in track_response['releases']: | |
| if 'versionCodes' in release: | |
| for vc in release['versionCodes']: | |
| max_version_code = max(max_version_code, int(vc)) | |
| except HttpError: | |
| continue | |
| # Delete the edit (cleanup) | |
| try: | |
| service.edits().delete(packageName=PACKAGE_NAME, editId=edit_id).execute() | |
| except: | |
| pass | |
| return max_version_code | |
| except Exception as e: | |
| print(f"Error querying Google Play API: {e}") | |
| print(traceback.format_exc()) | |
| return None | |
| def update_version_code(new_version_code): | |
| """Update version code in build.gradle.kts.""" | |
| gradle_file = "app/build.gradle.kts" | |
| with open(gradle_file, 'r') as f: | |
| content = f.read() | |
| # Find and replace versionCode | |
| pattern = r'versionCode\s*=\s*\d+' | |
| replacement = f'versionCode = {new_version_code}' | |
| new_content = re.sub(pattern, replacement, content) | |
| with open(gradle_file, 'w') as f: | |
| f.write(new_content) | |
| print(f"✓ Updated {gradle_file} with version code {new_version_code}") | |
| # Main logic | |
| print("Querying Google Play Console for latest version code...") | |
| max_version_code = get_latest_version_code_from_play() | |
| if max_version_code is not None and max_version_code > 0: | |
| new_version_code = max_version_code + 1 | |
| print(f"Latest version code found: {max_version_code}") | |
| print(f"New version code will be: {new_version_code}") | |
| update_version_code(new_version_code) | |
| else: | |
| print("Could not retrieve version code from Play Console, using fallback...") | |
| # Fallback: increment current version code by 1 | |
| gradle_file = "app/build.gradle.kts" | |
| with open(gradle_file, 'r') as f: | |
| content = f.read() | |
| match = re.search(r'versionCode\s*=\s*(\d+)', content) | |
| if match: | |
| current_version = int(match.group(1)) | |
| new_version = current_version + 1 | |
| update_version_code(new_version) | |
| print(f"Fallback: Incremented from {current_version} to {new_version}") | |
| else: | |
| print("ERROR: Could not find versionCode in build.gradle.kts") | |
| sys.exit(1) | |
| EOF | |
| - name: Build release bundle | |
| run: ./gradlew bundleRelease | |
| - name: Decode keystore (if provided) | |
| continue-on-error: true | |
| run: | | |
| echo "${{ secrets.ANDROID_SIGNING_KEYSTORE_BASE64 }}" | base64 --decode > release.keystore | |
| - name: Sign AAB (if secrets present) | |
| continue-on-error: true | |
| env: | |
| KEY_ALIAS: ${{ secrets.ANDROID_SIGNING_KEY_ALIAS }} | |
| KEY_PASSWORD: ${{ secrets.ANDROID_SIGNING_KEY_PASSWORD }} | |
| STORE_PASSWORD: ${{ secrets.ANDROID_SIGNING_KEYSTORE_PASSWORD }} | |
| run: | | |
| AAB_PATH=$(ls app/build/outputs/bundle/release/*.aab | head -n 1) | |
| if [ -z "$AAB_PATH" ]; then | |
| echo "No AAB produced at app/build/outputs/bundle/release." >&2 | |
| exit 1 | |
| fi | |
| if [ ! -f "release.keystore" ]; then | |
| echo "Keystore not found, skipping signing" | |
| exit 0 | |
| fi | |
| # Sign AAB using jarsigner | |
| jarsigner -verbose -sigalg SHA256withRSA -digestalg SHA-256 \ | |
| -keystore release.keystore \ | |
| -storepass "$STORE_PASSWORD" \ | |
| -keypass "$KEY_PASSWORD" \ | |
| "$AAB_PATH" \ | |
| "$KEY_ALIAS" | |
| # Verify the signature | |
| jarsigner -verify -verbose -certs "$AAB_PATH" | |
| - name: Set release variables | |
| id: release_vars | |
| run: | | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | |
| echo "play_track=${{ github.event.inputs.play_track || 'internal' }}" >> $GITHUB_OUTPUT | |
| echo "release_name=${{ github.event.inputs.release_name || github.ref_name }}" >> $GITHUB_OUTPUT | |
| else | |
| # Automatic upload to internal track for main branch pushes | |
| echo "play_track=internal" >> $GITHUB_OUTPUT | |
| echo "release_name=main-${GITHUB_SHA::7}" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Upload release artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: release-bundle | |
| path: app/build/outputs/bundle/release/*.aab | |
| - name: Upload to Google Play Console | |
| continue-on-error: true | |
| uses: r0adkll/upload-google-play@v1 | |
| with: | |
| serviceAccountJsonPlainText: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON }} | |
| packageName: llc.fungee.IngrediCheck | |
| releaseFiles: app/build/outputs/bundle/release/*.aab | |
| track: ${{ steps.release_vars.outputs.play_track }} | |
| status: completed | |