|
| 1 | +name: Build App |
| 2 | + |
| 3 | +on: |
| 4 | + workflow_call: |
| 5 | + inputs: |
| 6 | + app-type: |
| 7 | + description: 'App type (Parent, Student, or Teacher)' |
| 8 | + required: true |
| 9 | + type: string |
| 10 | + app-type-lower: |
| 11 | + description: 'App type in lowercase (parent, student, or teacher)' |
| 12 | + required: true |
| 13 | + type: string |
| 14 | + firebase-app-id-secret: |
| 15 | + description: 'Name of the Firebase App ID secret' |
| 16 | + required: true |
| 17 | + type: string |
| 18 | + secrets: |
| 19 | + ACCESS_TOKEN: |
| 20 | + required: true |
| 21 | + ANDROID_RELEASE_KEYSTORE_B64: |
| 22 | + required: true |
| 23 | + FIREBASE_SERVICE_ACCOUNT_KEY: |
| 24 | + required: true |
| 25 | + FIREBASE_APP_ID: |
| 26 | + required: true |
| 27 | + |
| 28 | +jobs: |
| 29 | + build: |
| 30 | + name: ${{ inputs.app-type-lower }}-build |
| 31 | + runs-on: ubuntu-latest |
| 32 | + steps: |
| 33 | + - name: Checkout repository |
| 34 | + uses: actions/checkout@v4 |
| 35 | + with: |
| 36 | + submodules: 'recursive' |
| 37 | + fetch-depth: 1 |
| 38 | + token: ${{ secrets.ACCESS_TOKEN }} |
| 39 | + |
| 40 | + - name: Set up JDK 17 |
| 41 | + uses: actions/setup-java@v4 |
| 42 | + with: |
| 43 | + java-version: '17' |
| 44 | + distribution: 'temurin' |
| 45 | + |
| 46 | + - name: Cache Gradle packages |
| 47 | + uses: actions/cache@v4 |
| 48 | + with: |
| 49 | + path: | |
| 50 | + ~/.gradle/caches |
| 51 | + ~/.gradle/wrapper |
| 52 | + key: ${{ runner.os }}-gradle-${{ hashFiles('**/gradle-wrapper.properties') }} |
| 53 | + restore-keys: | |
| 54 | + ${{ runner.os }}-gradle- |
| 55 | +
|
| 56 | + - name: Cache Gradle Build Cache |
| 57 | + uses: actions/cache@v4 |
| 58 | + with: |
| 59 | + path: | |
| 60 | + ~/.gradle/caches/build-cache-* |
| 61 | + .gradle |
| 62 | + key: ${{ runner.os }}-gradle-build-cache-${{ github.sha }} |
| 63 | + restore-keys: | |
| 64 | + ${{ runner.os }}-gradle-build-cache- |
| 65 | +
|
| 66 | + - name: Decode Release Keystore |
| 67 | + run: | |
| 68 | + echo "${{ secrets.ANDROID_RELEASE_KEYSTORE_B64 }}" | base64 --decode > release.jks |
| 69 | + chmod 600 release.jks |
| 70 | +
|
| 71 | + - name: Setup Service account |
| 72 | + env: |
| 73 | + FIREBASE_SERVICE_ACCOUNT_KEY: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_KEY }} |
| 74 | + run: | |
| 75 | + if [ -z "${FIREBASE_SERVICE_ACCOUNT_KEY}" ]; then |
| 76 | + echo "Error: Firebase service account key is not configured" |
| 77 | + exit 1 |
| 78 | + fi |
| 79 | + echo "${FIREBASE_SERVICE_ACCOUNT_KEY}" > service-account-key.json |
| 80 | + chmod 600 service-account-key.json |
| 81 | +
|
| 82 | + - name: Build Release Notes |
| 83 | + id: get_release_notes |
| 84 | + run: | |
| 85 | + echo "RELEASE_NOTES<<EOF" >> $GITHUB_OUTPUT |
| 86 | + echo "${{ github.event.pull_request.title }}" >> $GITHUB_OUTPUT |
| 87 | + echo "EOF" >> $GITHUB_OUTPUT |
| 88 | +
|
| 89 | + - name: Install Firebase CLI |
| 90 | + run: npm install -g firebase-tools |
| 91 | + |
| 92 | + - name: Setup Firebase App Id |
| 93 | + run: | |
| 94 | + if [ -z "${{ secrets.FIREBASE_APP_ID }}" ]; then |
| 95 | + echo "Error: Firebase App ID is not configured" |
| 96 | + exit 1 |
| 97 | + fi |
| 98 | + echo "${{ secrets.FIREBASE_APP_ID }}" > firebase_app_id.txt |
| 99 | +
|
| 100 | + - name: Build debug and test APKs |
| 101 | + run: | |
| 102 | + ./gradle/gradlew -p apps :${{ inputs.app-type-lower }}:assembleQaDebug \ |
| 103 | + :${{ inputs.app-type-lower }}:assembleQaDebugAndroidTest \ |
| 104 | + :${{ inputs.app-type-lower }}:assembleDevDebugMinify \ |
| 105 | + --build-cache \ |
| 106 | + --parallel \ |
| 107 | + --max-workers=4 \ |
| 108 | + --no-daemon \ |
| 109 | + -Dorg.gradle.jvmargs="-Xmx6g -XX:+HeapDumpOnOutOfMemoryError" \ |
| 110 | + -Dkotlin.compiler.execution.strategy=in-process \ |
| 111 | + -Pandroid.injected.signing.store.file=$(pwd)/release.jks |
| 112 | +
|
| 113 | + - name: Upload QA debug APK |
| 114 | + uses: actions/upload-artifact@v4 |
| 115 | + with: |
| 116 | + name: ${{ inputs.app-type-lower }}-qa-debug.apk |
| 117 | + path: apps/${{ inputs.app-type-lower }}/build/outputs/apk/qa/debug/${{ inputs.app-type-lower }}-qa-debug.apk |
| 118 | + |
| 119 | + - name: Upload QA test APK |
| 120 | + uses: actions/upload-artifact@v4 |
| 121 | + with: |
| 122 | + name: ${{ inputs.app-type-lower }}-qa-debug-androidTest.apk |
| 123 | + path: apps/${{ inputs.app-type-lower }}/build/outputs/apk/androidTest/qa/debug/${{ inputs.app-type-lower }}-qa-debug-androidTest.apk |
| 124 | + |
| 125 | + - name: Upload Dev debug APK |
| 126 | + uses: actions/upload-artifact@v4 |
| 127 | + with: |
| 128 | + name: ${{ inputs.app-type-lower }}-dev-debugMinify.apk |
| 129 | + path: apps/${{ inputs.app-type-lower }}/build/outputs/apk/dev/debugMinify/${{ inputs.app-type-lower }}-dev-debugMinify.apk |
| 130 | + |
| 131 | + - name: Distribute app to Firebase App Distribution |
| 132 | + env: |
| 133 | + GOOGLE_APPLICATION_CREDENTIALS: ${{ github.workspace }}/service-account-key.json |
| 134 | + run: | |
| 135 | + firebase --version |
| 136 | + FIREBASE_APP_ID="$(cat firebase_app_id.txt)" |
| 137 | +
|
| 138 | + if ! firebase appdistribution:distribute "apps/${{ inputs.app-type-lower }}/build/outputs/apk/dev/debugMinify/${{ inputs.app-type-lower }}-dev-debugMinify.apk" \ |
| 139 | + --app "$FIREBASE_APP_ID" \ |
| 140 | + --release-notes "${{ steps.get_release_notes.outputs.RELEASE_NOTES }}" \ |
| 141 | + --groups "Testers" > result.txt 2>&1; then |
| 142 | + echo "Firebase distribution failed:" |
| 143 | + cat result.txt |
| 144 | + exit 1 |
| 145 | + fi |
| 146 | + cat result.txt |
| 147 | +
|
| 148 | + - name: Prepare Comment Body |
| 149 | + id: prepare_comment |
| 150 | + run: | |
| 151 | + INSTALL_URL=$(grep -o 'https://appdistribution\.firebase[^[:space:]]*' result.txt | head -1) |
| 152 | +
|
| 153 | + if [ -z "$INSTALL_URL" ]; then |
| 154 | + echo "Error: Could not extract install URL from Firebase output" |
| 155 | + cat result.txt |
| 156 | + exit 1 |
| 157 | + fi |
| 158 | +
|
| 159 | + INSTALL_URL_ESCAPED=$(printf '%s' "$INSTALL_URL" | sed 's/:/%3A/g; s/\//%2F/g; s/?/%3F/g; s/=/%3D/g; s/&/%26/g') |
| 160 | + { |
| 161 | + echo "body<<EOF" |
| 162 | + echo "<!-- qr-code-${{ inputs.app-type }} -->" |
| 163 | + echo "<p>${{ inputs.app-type }} Install Page</p><img src=\"https://api.qrserver.com/v1/create-qr-code/?data=${INSTALL_URL_ESCAPED}\" width=\"200\">" |
| 164 | + echo "EOF" |
| 165 | + } >> $GITHUB_OUTPUT |
| 166 | +
|
| 167 | + - name: Find Previous Comment |
| 168 | + id: find_comment |
| 169 | + uses: peter-evans/find-comment@v2 |
| 170 | + with: |
| 171 | + issue-number: ${{ github.event.pull_request.number }} |
| 172 | + comment-author: 'github-actions[bot]' |
| 173 | + body-includes: '<!-- qr-code-${{ inputs.app-type }} -->' |
| 174 | + |
| 175 | + - name: Create or Update Comment |
| 176 | + uses: peter-evans/create-or-update-comment@v4 |
| 177 | + with: |
| 178 | + comment-id: ${{ steps.find_comment.outputs.comment-id }} |
| 179 | + issue-number: ${{ github.event.pull_request.number }} |
| 180 | + body: ${{ steps.prepare_comment.outputs.body }} |
| 181 | + edit-mode: replace |
| 182 | + |
| 183 | + - name: Cleanup sensitive files |
| 184 | + if: always() |
| 185 | + run: | |
| 186 | + rm -f release.jks |
| 187 | + rm -f service-account-key.json |
| 188 | + rm -f firebase_app_id.txt |
| 189 | + rm -f result.txt |
0 commit comments