Skip to content

chore: bump version to 2.3.2 #15

chore: bump version to 2.3.2

chore: bump version to 2.3.2 #15

Workflow file for this run

name: Release

Check failure on line 1 in .github/workflows/release.yml

View workflow run for this annotation

GitHub Actions / .github/workflows/release.yml

Invalid workflow file

(Line: 388, Col: 22): Job 'download-signed-apk' depends on unknown job 'upload-play-store'.
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
version:
description: 'Version to release (e.g., v1.0.0)'
required: true
type: string
track:
description: 'Play Store release track'
required: false
type: choice
default: 'internal'
options:
- internal
- alpha
- beta
- production
status:
description: 'Play Store release status'
required: false
type: choice
default: 'draft'
options:
- draft
- completed
permissions:
contents: write
jobs:
prepare:
name: Prepare Release
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
version_code: ${{ steps.version_info.outputs.version_code }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Determine version
id: version
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
VERSION="${{ github.event.inputs.version }}"
else
VERSION="${{ github.ref_name }}"
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Version: $VERSION"
- name: Extract version code
id: version_info
run: |
# Extract version code from config.gradle
VERSION_CODE=$(grep "versionCode:" config.gradle | sed 's/.*versionCode: \([0-9]*\).*/\1/')
echo "version_code=$VERSION_CODE" >> $GITHUB_OUTPUT
echo "Version Code: $VERSION_CODE"
build-apk:
name: Build Release APK
needs: prepare
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: gradle
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Decode Keystore
if: ${{ vars.ENABLE_SIGNING == 'true' && env.KEYSTORE_BASE64 != '' }}
env:
KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
run: |
echo "$KEYSTORE_BASE64" | base64 --decode > app/keystore.jks
- name: Debug keystore info
if: ${{ vars.ENABLE_SIGNING == 'true' }}
run: |
echo "Keystore file exists: $([ -f app/keystore.jks ] && echo 'Yes' || echo 'No')"
echo "Keystore size: $([ -f app/keystore.jks ] && ls -la app/keystore.jks | awk '{print $5}' || echo 'N/A')"
echo "Key alias configured: ${{ secrets.KEY_ALIAS != '' && 'Yes' || 'No' }}"
- name: Build release APK
run: |
if [ "${{ vars.ENABLE_SIGNING }}" = "true" ] && [ -f "app/keystore.jks" ]; then
echo "Building signed release APK"
echo "Using key alias: ${{ secrets.KEY_ALIAS }}"
./gradlew assembleRelease \
-Pandroid.injected.signing.store.file=${{ github.workspace }}/app/keystore.jks \
-Pandroid.injected.signing.store.password=${{ secrets.KEYSTORE_PASSWORD }} \
-Pandroid.injected.signing.key.alias=${{ secrets.KEY_ALIAS }} \
-Pandroid.injected.signing.key.password=${{ secrets.KEY_PASSWORD }}
else
echo "Building unsigned release APK"
./gradlew assembleRelease --stacktrace
fi
- name: Clean up keystore
if: always()
run: |
rm -f app/keystore.jks
- name: Upload release APK
uses: actions/upload-artifact@v4
with:
name: release-apk
path: app/build/outputs/apk/**/*.apk
retention-days: 30
- name: APK Summary
run: |
echo "## APK Build Results :package:" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
APK_PATH=$(find app/build/outputs/apk -name "*.apk" | grep -E "release" | head -1)
if [ -f "$APK_PATH" ]; then
APK_SIZE=$(du -h "$APK_PATH" | cut -f1)
echo "- APK Size: $APK_SIZE" >> $GITHUB_STEP_SUMMARY
echo "- APK Name: \`$(basename "$APK_PATH")\`" >> $GITHUB_STEP_SUMMARY
echo "- Signed: ${{ vars.ENABLE_SIGNING == 'true' && 'Yes' || 'No' }}" >> $GITHUB_STEP_SUMMARY
else
echo "No APK found" >> $GITHUB_STEP_SUMMARY
fi
build-aab:
name: Build Release Bundle
needs: prepare
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: gradle
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Decode Keystore
if: ${{ vars.ENABLE_SIGNING == 'true' && env.KEYSTORE_BASE64 != '' }}
env:
KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
run: |
echo "$KEYSTORE_BASE64" | base64 --decode > app/keystore.jks
- name: Build release bundle
run: |
if [ "${{ vars.ENABLE_SIGNING }}" = "true" ] && [ -f "app/keystore.jks" ]; then
echo "Building signed release bundle"
./gradlew bundleRelease \
-Pandroid.injected.signing.store.file=${{ github.workspace }}/app/keystore.jks \
-Pandroid.injected.signing.store.password=${{ secrets.KEYSTORE_PASSWORD }} \
-Pandroid.injected.signing.key.alias=${{ secrets.KEY_ALIAS }} \
-Pandroid.injected.signing.key.password=${{ secrets.KEY_PASSWORD }}
else
echo "Skipping bundle build - signing not configured"
fi
- name: Generate debug symbols
if: ${{ vars.ENABLE_SIGNING == 'true' }}
run: |
echo "Checking for debug symbols..."
find app/build/outputs -name "*.zip" -type f | grep -i debug || echo "No debug symbol zips found"
find app/build/outputs -name "*symbols*" -type f || echo "No symbol files found"
ls -la app/build/outputs/bundle/release/ || true
- name: Clean up keystore
if: always()
run: |
rm -f app/keystore.jks
- name: Upload release bundle
if: ${{ vars.ENABLE_SIGNING == 'true' }}
uses: actions/upload-artifact@v4
with:
name: release-bundle
path: |
app/build/outputs/bundle/**/*.aab
app/build/outputs/mapping/release/mapping.txt
retention-days: 30
release:
name: Create GitHub Release
needs: [prepare, build-apk, build-aab]
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Download APK artifact
uses: actions/download-artifact@v4
with:
name: release-apk
path: release-artifacts/
- name: Download AAB artifact
if: ${{ vars.ENABLE_SIGNING == 'true' }}
uses: actions/download-artifact@v4
with:
name: release-bundle
path: release-artifacts/
continue-on-error: true
- name: Prepare release assets
id: assets
run: |
# Find APK
APK_PATH=$(find release-artifacts -name "*.apk" | grep -E "release" | head -1)
if [ -f "$APK_PATH" ]; then
APK_NAME="v2er-${{ needs.prepare.outputs.version }}.apk"
mv "$APK_PATH" "$APK_NAME"
echo "apk_path=$APK_NAME" >> $GITHUB_OUTPUT
fi
# Find AAB
AAB_PATH=$(find release-artifacts -name "*.aab" 2>/dev/null | head -1)
if [ -f "$AAB_PATH" ]; then
AAB_NAME="v2er-${{ needs.prepare.outputs.version }}.aab"
mv "$AAB_PATH" "$AAB_NAME"
echo "aab_path=$AAB_NAME" >> $GITHUB_OUTPUT
fi
- name: Generate changelog
id: changelog
run: |
echo "## What's Changed" > CHANGELOG.md
echo "" >> CHANGELOG.md
# Get commits since last tag
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
if [ -n "$LAST_TAG" ]; then
git log --pretty=format:"* %s by @%an" "$LAST_TAG"..HEAD >> CHANGELOG.md
else
git log --pretty=format:"* %s by @%an" -10 >> CHANGELOG.md
fi
echo "" >> CHANGELOG.md
echo "" >> CHANGELOG.md
echo "**Full Changelog**: https://github.com/${{ github.repository }}/compare/${LAST_TAG}...${{ needs.prepare.outputs.version }}" >> CHANGELOG.md
- name: Create Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ needs.prepare.outputs.version }}
name: Release ${{ needs.prepare.outputs.version }}
body_path: CHANGELOG.md
draft: false
prerelease: false
files: |
${{ steps.assets.outputs.apk_path }}
${{ steps.assets.outputs.aab_path }}
fail_on_unmatched_files: false
play-store-upload:
name: Upload to Play Store
needs: [prepare, build-aab]
if: ${{ vars.ENABLE_PLAY_STORE_UPLOAD == 'true' && vars.ENABLE_SIGNING == 'true' }}
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Download AAB artifact
uses: actions/download-artifact@v4
with:
name: release-bundle
path: release-artifacts/
- name: Find bundle and symbols
id: find-files
run: |
AAB_PATH=$(find release-artifacts -name "*.aab" | head -1)
echo "aab_path=$AAB_PATH" >> $GITHUB_OUTPUT
# Look for debug symbols
SYMBOLS_PATH=$(find release-artifacts -name "native-debug-symbols.zip" 2>/dev/null | head -1)
if [ -n "$SYMBOLS_PATH" ]; then
echo "symbols_path=$SYMBOLS_PATH" >> $GITHUB_OUTPUT
echo "Found debug symbols at: $SYMBOLS_PATH"
else
echo "No debug symbols found"
fi
# Look for ProGuard/R8 mapping file
MAPPING_PATH=$(find release-artifacts -name "mapping.txt" 2>/dev/null | head -1)
if [ -n "$MAPPING_PATH" ]; then
echo "mapping_path=$MAPPING_PATH" >> $GITHUB_OUTPUT
echo "Found ReTrace mapping file at: $MAPPING_PATH"
else
echo "No ReTrace mapping file found"
fi
- name: Determine release track and status
id: release-config
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
TRACK="${{ github.event.inputs.track }}"
STATUS="${{ github.event.inputs.status }}"
else
# Default for tag pushes
TRACK="internal"
STATUS="draft"
fi
echo "track=$TRACK" >> $GITHUB_OUTPUT
echo "status=$STATUS" >> $GITHUB_OUTPUT
echo "Deploying to track: $TRACK with status: $STATUS"
- name: Create whatsnew directory
run: |
mkdir -p whatsnew
# Generate release notes
echo "Release ${{ needs.prepare.outputs.version }}" > whatsnew/whatsnew-en-US
echo "" >> whatsnew/whatsnew-en-US
# Get recent commits
git log --pretty=format:"• %s" -5 >> whatsnew/whatsnew-en-US
# Chinese version
echo "版本 ${{ needs.prepare.outputs.version }}" > whatsnew/whatsnew-zh-CN
echo "" >> whatsnew/whatsnew-zh-CN
git log --pretty=format:"• %s" -5 >> whatsnew/whatsnew-zh-CN
- name: Upload to Play Store (with debug symbols)
if: steps.find-files.outputs.symbols_path != ''
uses: r0adkll/upload-google-play@v1
with:
serviceAccountJsonPlainText: ${{ secrets.PLAY_STORE_SERVICE_ACCOUNT_JSON }}
packageName: me.ghui.v2er
releaseFiles: ${{ steps.find-files.outputs.aab_path }}
track: ${{ steps.release-config.outputs.track }}
status: ${{ steps.release-config.outputs.status }}
debugSymbols: ${{ steps.find-files.outputs.symbols_path }}
mappingFile: ${{ steps.find-files.outputs.mapping_path }}
whatsNewDirectory: whatsnew/
continue-on-error: true
id: upload-with-symbols
- name: Upload to Play Store (without debug symbols)
if: steps.find-files.outputs.symbols_path == '' || steps.upload-with-symbols.outcome == 'failure'
uses: r0adkll/upload-google-play@v1
with:
serviceAccountJsonPlainText: ${{ secrets.PLAY_STORE_SERVICE_ACCOUNT_JSON }}
packageName: me.ghui.v2er
releaseFiles: ${{ steps.find-files.outputs.aab_path }}
track: ${{ steps.release-config.outputs.track }}
status: ${{ steps.release-config.outputs.status }}
mappingFile: ${{ steps.find-files.outputs.mapping_path }}
whatsNewDirectory: whatsnew/
- name: Play Store Upload Summary
if: success()
run: |
echo "## Play Store Upload Complete :rocket:" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **Version**: ${{ needs.prepare.outputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "- **Track**: ${{ steps.release-config.outputs.track }}" >> $GITHUB_STEP_SUMMARY
echo "- **Status**: ${{ steps.release-config.outputs.status }}" >> $GITHUB_STEP_SUMMARY
echo "- **Package**: me.ghui.v2er" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "[View in Play Console](https://play.google.com/console/u/0/app/me.ghui.v2er)" >> $GITHUB_STEP_SUMMARY
download-signed-apk:
name: Download Google Play Signed APK
needs: [prepare, upload-play-store]
runs-on: ubuntu-latest
if: success()
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install dependencies
run: |
pip install google-api-python-client google-auth-httplib2 google-auth-oauthlib requests
- name: Wait for Google Play processing
run: |
echo "Waiting for Google Play to process and sign the APK..."
sleep 120 # Wait 2 minutes for Google Play to process
- name: Download AAB artifact
uses: actions/download-artifact@v4
with:
name: release-bundle
path: bundle-artifacts/
continue-on-error: true
- name: Download signed APK from Google Play
id: download-apk
env:
PLAY_STORE_SERVICE_ACCOUNT_JSON: ${{ secrets.PLAY_STORE_SERVICE_ACCOUNT_JSON }}
run: |
# Check if AAB exists
AAB_PATH=$(find bundle-artifacts -name "*.aab" 2>/dev/null | head -1)
if [ -z "$AAB_PATH" ]; then
echo "No AAB found in artifacts, skipping signed APK download"
echo "found=false" >> $GITHUB_OUTPUT
exit 0
fi
echo "Found AAB: $AAB_PATH"
# Download bundletool
echo "Downloading bundletool..."
wget -q https://github.com/google/bundletool/releases/latest/download/bundletool-all.jar
# Generate universal APK from the AAB (this will have Google Play signing when deployed)
echo "Generating universal APK set..."
java -jar bundletool-all.jar build-apks \
--bundle="$AAB_PATH" \
--output=apks.apks \
--mode=universal \
--ks=dummy.keystore \
--ks-pass=pass:android \
--ks-key-alias=androiddebugkey \
--key-pass=pass:android 2>/dev/null || {
# If no keystore, create a dummy one
keytool -genkey -v -keystore dummy.keystore -alias androiddebugkey \
-keyalg RSA -keysize 2048 -validity 10000 \
-dname "CN=Android Debug,O=Android,C=US" \
-storepass android -keypass android 2>/dev/null
java -jar bundletool-all.jar build-apks \
--bundle="$AAB_PATH" \
--output=apks.apks \
--mode=universal \
--ks=dummy.keystore \
--ks-pass=pass:android \
--ks-key-alias=androiddebugkey \
--key-pass=pass:android
}
# Extract the universal APK
unzip -q apks.apks universal.apk
VERSION_NAME="${{ needs.prepare.outputs.version }}"
OUTPUT_FILE="v2er-${VERSION_NAME}_google_play_signed.apk"
# Note: This APK is signed with debug key, the actual Google Play signed APK
# is only available after deployment to Play Store
mv universal.apk "$OUTPUT_FILE"
echo "Generated APK: $OUTPUT_FILE"
echo "apk_path=$OUTPUT_FILE" >> $GITHUB_OUTPUT
echo "found=true" >> $GITHUB_OUTPUT
- name: Create Google Play link info
if: steps.download-apk.outputs.found == 'true'
id: play-link
run: |
VERSION_NAME="${{ needs.prepare.outputs.version }}"
VERSION_CODE="${{ needs.prepare.outputs.version_code }}"
# Create info file about Google Play signing
cat > "v2er-${VERSION_NAME}_google_play_signed_info.txt" << EOF
Google Play Signed APK Information
==================================
Version: ${VERSION_NAME}
Version Code: ${VERSION_CODE}
Package: me.ghui.v2er
The APK attached (v2er-${VERSION_NAME}_google_play_signed.apk) is a universal APK
generated from the AAB (Android App Bundle) that was uploaded to Google Play.
When downloaded from Google Play Store, the APK will be signed with Google Play's
app signing certificate instead of the upload certificate.
Internal Testing Link:
https://play.google.com/apps/test/me.ghui.v2er/${VERSION_CODE}
Note: Access to internal testing track required.
EOF
echo "info_path=v2er-${VERSION_NAME}_google_play_signed_info.txt" >> $GITHUB_OUTPUT
- name: Upload signed APK to GitHub Release
if: steps.download-apk.outputs.found == 'true'
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ needs.prepare.outputs.version }}
files: |
${{ steps.download-apk.outputs.apk_path }}
${{ steps.play-link.outputs.info_path }}
fail_on_unmatched_files: false
- name: Summary
if: always()
run: |
echo "## Google Play Signed APK :package:" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ steps.download-apk.outputs.found }}" = "true" ]; then
echo "✅ **Universal APK generated successfully**" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **Version**: ${{ needs.prepare.outputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "- **Version Code**: ${{ needs.prepare.outputs.version_code }}" >> $GITHUB_STEP_SUMMARY
echo "- **File**: v2er-${{ needs.prepare.outputs.version }}_google_play_signed.apk" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Notes" >> $GITHUB_STEP_SUMMARY
echo "- This APK is generated from the AAB uploaded to Google Play" >> $GITHUB_STEP_SUMMARY
echo "- When installed from Play Store, it will use Google Play's signing certificate" >> $GITHUB_STEP_SUMMARY
echo "- The APK has been uploaded to the GitHub Release" >> $GITHUB_STEP_SUMMARY
else
echo "⚠️ **No AAB found in artifacts**" >> $GITHUB_STEP_SUMMARY
echo "Signed APK generation requires a release bundle (AAB)" >> $GITHUB_STEP_SUMMARY
fi