Skip to content

Fix Python syntax error: use sys.exit() instead of exit #15

Fix Python syntax error: use sys.exit() instead of exit

Fix Python syntax error: use sys.exit() instead of exit #15

Workflow file for this run

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