Build Signed Android APK/AAB #12
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 Signed Android APK/AAB | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| build_type: | |
| description: 'Build type' | |
| required: true | |
| default: 'release' | |
| type: choice | |
| options: | |
| - debug | |
| - release | |
| push: | |
| tags: | |
| - 'v*.*.*' | |
| jobs: | |
| build-android: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| - name: Set up JDK 17 | |
| uses: actions/setup-java@v4 | |
| with: | |
| distribution: 'temurin' | |
| java-version: '17' | |
| - name: Install dependencies | |
| run: npm install --legacy-peer-deps | |
| - name: Expo Prebuild with signing | |
| run: npx expo prebuild --platform android --clean | |
| - name: Decode Keystore | |
| run: | | |
| echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 --decode > android/app/release.keystore | |
| - name: Setup Gradle cache | |
| uses: gradle/actions/setup-gradle@v3 | |
| with: | |
| gradle-home-cache-cleanup: true | |
| - name: Create keystore.properties | |
| run: | | |
| echo "storeFile=release.keystore" > android/keystore.properties | |
| echo "storePassword=${{ secrets.KEYSTORE_PASSWORD }}" >> android/keystore.properties | |
| echo "keyAlias=${{ secrets.KEY_ALIAS }}" >> android/keystore.properties | |
| echo "keyPassword=${{ secrets.KEY_PASSWORD }}" >> android/keystore.properties | |
| - name: Configure Gradle for release signing | |
| run: | | |
| # Inject signing config into build.gradle | |
| cat >> android/app/build.gradle << 'EOF' | |
| android.signingConfigs { | |
| release { | |
| def keystorePropertiesFile = rootProject.file("keystore.properties") | |
| if (keystorePropertiesFile.exists()) { | |
| def keystoreProperties = new Properties() | |
| keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) | |
| storeFile file(keystoreProperties['storeFile']) | |
| storePassword keystoreProperties['storePassword'] | |
| keyAlias keystoreProperties['keyAlias'] | |
| keyPassword keystoreProperties['keyPassword'] | |
| } | |
| } | |
| } | |
| android.buildTypes.release.signingConfig = android.signingConfigs.release | |
| EOF | |
| - name: Make gradlew executable | |
| run: chmod +x android/gradlew | |
| - name: Build Signed APK | |
| run: cd android && ./gradlew assembleRelease --no-daemon --stacktrace | |
| - name: Build Signed AAB | |
| run: cd android && ./gradlew bundleRelease --no-daemon --stacktrace | |
| - name: Verify APK signature | |
| run: | | |
| APK_PATH=$(find android/app/build/outputs/apk/release -name "*.apk" | head -1) | |
| echo "Checking signature of: $APK_PATH" | |
| unzip -p "$APK_PATH" META-INF/CERT.RSA | keytool -printcert | grep SHA1 | |
| - name: Upload APK artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: app-release-apk | |
| path: android/app/build/outputs/apk/release/*.apk | |
| retention-days: 30 | |
| - name: Upload AAB artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: app-release-aab | |
| path: android/app/build/outputs/bundle/release/*.aab | |
| retention-days: 30 | |
| - name: Cleanup keystore | |
| if: always() | |
| run: | | |
| rm -f android/app/release.keystore | |
| rm -f android/keystore.properties |