Build and Release Android App #7
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: Build and Release Android App | |
| on: | |
| push: | |
| tags: | |
| - 'v*' # Trigger on version tags like v1.0.0 | |
| workflow_dispatch: # Allow manual trigger | |
| jobs: | |
| build: | |
| name: Build and Release APK | |
| 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' | |
| - name: Setup Android SDK | |
| uses: android-actions/setup-android@v3 | |
| - name: Cache Gradle packages | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.gradle/caches | |
| ~/.gradle/wrapper | |
| key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} | |
| restore-keys: | | |
| ${{ runner.os }}-gradle- | |
| - name: Make gradlew executable | |
| run: chmod +x ./gradlew | |
| - name: Generate debug keystore | |
| run: | | |
| mkdir -p $HOME/.android | |
| echo "storePassword=android" > keystore.properties | |
| echo "keyPassword=android" >> keystore.properties | |
| echo "keyAlias=androiddebugkey" >> keystore.properties | |
| echo "storeFile=$HOME/.android/debug.keystore" >> keystore.properties | |
| # Generate debug keystore | |
| keytool -genkey -v -keystore $HOME/.android/debug.keystore \ | |
| -storepass android -alias androiddebugkey -keypass android \ | |
| -keyalg RSA -keysize 2048 -validity 10000 \ | |
| -dname "CN=Android Debug,O=Android,C=US" | |
| - name: Build debug APK | |
| run: ./gradlew assembleDebug | |
| env: | |
| SIGNING_KEY_ALIAS: androiddebugkey | |
| SIGNING_KEY_PASSWORD: android | |
| SIGNING_STORE_PASSWORD: android | |
| - name: Sign APK | |
| run: | | |
| # Find the unsigned APK | |
| APK_PATH=$(find app/build/outputs/apk/debug -name "*.apk" | head -1) | |
| # Sign the APK using apksigner | |
| $ANDROID_HOME/build-tools/34.0.0/apksigner sign \ | |
| --ks $HOME/.android/debug.keystore \ | |
| --ks-key-alias androiddebugkey \ | |
| --ks-pass pass:android \ | |
| --key-pass pass:android \ | |
| --out app/build/outputs/apk/debug/app-debug-signed.apk \ | |
| $APK_PATH | |
| # Verify the signature | |
| $ANDROID_HOME/build-tools/34.0.0/apksigner verify app/build/outputs/apk/debug/app-debug-signed.apk | |
| - name: Get version from tag | |
| id: version | |
| run: | | |
| if [ "${{ github.ref_type }}" == "tag" ]; then | |
| echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT | |
| else | |
| echo "version=debug-$(date +%Y%m%d-%H%M%S)" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Rename APK | |
| run: | | |
| mv app/build/outputs/apk/debug/app-debug-signed.apk \ | |
| app/build/outputs/apk/debug/app-${{ steps.version.outputs.version }}.apk | |
| - name: Get APK info | |
| id: apk_info | |
| run: | | |
| APK_PATH="app/build/outputs/apk/debug/app-${{ steps.version.outputs.version }}.apk" | |
| APK_SIZE=$(stat -c%s "$APK_PATH") | |
| APK_SIZE_MB=$(echo "scale=2; $APK_SIZE / 1024 / 1024" | bc) | |
| echo "apk_path=$APK_PATH" >> $GITHUB_OUTPUT | |
| echo "apk_size=${APK_SIZE_MB}MB" >> $GITHUB_OUTPUT | |
| echo "apk_name=app-${{ steps.version.outputs.version }}.apk" >> $GITHUB_OUTPUT | |
| - name: Create Release with APK | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| # Create release body | |
| cat > release_body.md << 'EOF' | |
| ## 📱 Android App Release ${{ steps.version.outputs.version }} | |
| ### 📦 Download | |
| - **APK Size:** ${{ steps.apk_info.outputs.apk_size }} | |
| - **Build Type:** Debug (signed with debug certificate) | |
| - **Min SDK:** Check your app's build.gradle | |
| - **Target SDK:** Check your app's build.gradle | |
| ### 📋 Installation Instructions | |
| 1. Download the APK from the assets below | |
| 2. Enable "Install from unknown sources" in your Android settings | |
| 3. Install the APK file | |
| ### ⚠️ Important Notes | |
| - This is a debug build signed with a debug certificate | |
| - You may need to uninstall previous versions if they were signed with different certificates | |
| - The app will show as "unsigned" but it is properly signed for installation | |
| ### 🔧 Build Info | |
| - Built on: $(date -u '+%Y-%m-%d %H:%M:%S UTC') | |
| - Commit: ${{ github.sha }} | |
| - Workflow: ${{ github.run_number }} | |
| EOF | |
| # Create release with APK | |
| gh release create "${{ steps.version.outputs.version }}" \ | |
| "${{ steps.apk_info.outputs.apk_path }}" \ | |
| --title "Release ${{ steps.version.outputs.version }}" \ | |
| --notes-file release_body.md \ | |
| --draft=false \ | |
| --prerelease=false | |
| - name: Upload APK as Artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: signed-apk-${{ steps.version.outputs.version }} | |
| path: ${{ steps.apk_info.outputs.apk_path }} | |
| retention-days: 30 | |
| # Optional: Build unsigned APK for development | |
| build-unsigned: | |
| name: Build Unsigned APK (Development) | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'workflow_dispatch' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up JDK 17 | |
| uses: actions/setup-java@v4 | |
| with: | |
| java-version: '17' | |
| distribution: 'temurin' | |
| - name: Setup Android SDK | |
| uses: android-actions/setup-android@v3 | |
| - name: Make gradlew executable | |
| run: chmod +x ./gradlew | |
| - name: Build debug APK (unsigned) | |
| run: ./gradlew assembleDebug | |
| - name: Upload unsigned APK as Artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: unsigned-apk-debug | |
| path: app/build/outputs/apk/debug/*.apk | |
| retention-days: 7 |