Build and upload release version to Firebase App Distribution #277
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 upload release version to Firebase App Distribution | |
| permissions: | |
| id-token: write | |
| contents: write | |
| actions: write | |
| on: | |
| schedule: | |
| - cron: '0 12 * * *' # Daily at 12:00 PM UTC | |
| workflow_dispatch: | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| IDE_SIGNING_ALIAS: ${{ secrets.IDE_SIGNING_ALIAS }} | |
| IDE_SIGNING_AUTH_PASS: ${{ secrets.IDE_SIGNING_AUTH_PASS }} | |
| IDE_SIGNING_AUTH_USER: ${{ secrets.IDE_SIGNING_AUTH_USER }} | |
| IDE_SIGNING_KEY_PASS: ${{ secrets.IDE_SIGNING_KEY_PASS }} | |
| IDE_SIGNING_STORE_PASS: ${{ secrets.IDE_SIGNING_STORE_PASS }} | |
| IDE_SIGNING_URL: ${{ secrets.IDE_SIGNING_URL }} | |
| IDE_SIGNING_KEY_BIN: ${{ secrets.IDE_SIGNING_KEY_BIN }} | |
| ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MVN_USERNAME }} | |
| ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MVN_PASSWORD }} | |
| ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.MVN_SIGNING_KEY }} | |
| ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.MVN_SIGNING_KEY_ID }} | |
| ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.MVN_SIGNING_KEY_PASSWORD }} | |
| FIREBASE_CONSOLE_URL: ${{ secrets.FIREBASE_CONSOLE_URL }} | |
| SENTRY_DSN_RELEASE: ${{ secrets.SENTRY_DSN_RELEASE }} | |
| SENTRY_ORG: ${{ secrets.SENTRY_ORG }} | |
| SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} | |
| SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} | |
| jobs: | |
| merge_stage_to_main: | |
| name: Merge stage to main | |
| runs-on: self-hosted | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Cancel previous runs | |
| uses: styfle/cancel-workflow-action@0.12.1 | |
| with: | |
| access_token: ${{ github.token }} | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Configure Git | |
| run: | | |
| git config user.name "ADFA" | |
| git config user.email "dev-team@appdevforall.org" | |
| - name: Merge stage to main | |
| run: | | |
| git fetch origin stage | |
| git checkout main | |
| if ! git merge origin/stage --no-ff -m "Daily merge from stage to main"; then | |
| echo "Merge failed due to conflicts. Sending notification and stopping workflow." | |
| CONFLICTED_FILES=$(git diff --name-only --diff-filter=U | tr '\n' ' ') | |
| git merge --abort | |
| jq -n \ | |
| --arg conflicted_files "$CONFLICTED_FILES" \ | |
| '{ | |
| blocks: [ | |
| { | |
| type: "header", | |
| text: { | |
| type: "plain_text", | |
| text: ":warning: Daily Merge Failed - Conflicts Detected", | |
| emoji: true | |
| } | |
| }, | |
| { | |
| type: "section", | |
| text: { | |
| type: "mrkdwn", | |
| text: "@here The daily merge from `stage` to `main` failed due to merge conflicts." | |
| } | |
| }, | |
| { | |
| type: "section", | |
| text: { | |
| type: "mrkdwn", | |
| text: "*Conflicted Files:*\n```\($conflicted_files)```" | |
| } | |
| }, | |
| { | |
| type: "section", | |
| text: { | |
| type: "mrkdwn", | |
| text: "*Action Required:*\n1. Review the conflicts manually\n2. Resolve conflicts in the affected files\n3. Push the resolved changes to `stage`\n4. The next daily build will attempt the merge again" | |
| } | |
| }, | |
| { | |
| type: "actions", | |
| elements: [ | |
| { | |
| type: "button", | |
| text: { | |
| type: "plain_text", | |
| text: "View Repository", | |
| emoji: true | |
| }, | |
| url: "https://github.com/${{ github.repository }}", | |
| action_id: "view-repo" | |
| } | |
| ] | |
| }, | |
| { | |
| type: "divider" | |
| }, | |
| { | |
| type: "context", | |
| elements: [ | |
| { | |
| type: "mrkdwn", | |
| text: "Failed merge attempt from `stage` to `main` branch" | |
| } | |
| ] | |
| } | |
| ] | |
| }' > conflict_payload.json | |
| curl -X POST -H "Content-type: application/json" --data @conflict_payload.json "${{ secrets.SLACK_WEBHOOK_URL }}" | |
| rm -f conflict_payload.json | |
| echo "Merge conflicts detected in files: $CONFLICTED_FILES" | |
| exit 1 | |
| fi | |
| git push origin main | |
| echo "Merge completed successfully" | |
| download_assets: | |
| name: Download release assets | |
| runs-on: self-hosted | |
| timeout-minutes: 10 | |
| needs: merge_stage_to_main | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: main | |
| - name: Check if Nix is installed | |
| id: check_nix | |
| run: | | |
| if command -v nix >/dev/null 2>&1; then | |
| echo "nix is installed" | |
| echo "nix_installed=true" >> $GITHUB_ENV | |
| else | |
| echo "nix is not installed" | |
| echo "nix_installed=false" >> $GITHUB_ENV | |
| fi | |
| - name: Install Flox | |
| if: env.nix_installed == 'false' | |
| uses: flox/install-flox-action@v2 | |
| - name: Set up SSH key | |
| env: | |
| GREENGEEKS_HOST: ${{ vars.GREENGEEKS_SSH_HOST }} | |
| GREENGEEKS_KEY: ${{ secrets.GREENGEEKS_SSH_PRIVATE_KEY }} | |
| GREENGEEKS_USER: ${{ vars.GREENGEEKS_SSH_USER }} | |
| run: | | |
| mkdir -p ~/.ssh | |
| if [ -z "$GREENGEEKS_HOST" ]; then | |
| echo "Error: SSH_HOST variable is not set" | |
| exit 1 | |
| fi | |
| # Write the SSH key, ensuring proper formatting | |
| echo "$GREENGEEKS_KEY" > ~/.ssh/id_rsa | |
| # Remove any trailing newlines and ensure proper key format | |
| sed -i '' -e '$ { /^$/ d; }' ~/.ssh/id_rsa 2>/dev/null || sed -i '$ { /^$/ d; }' ~/.ssh/id_rsa | |
| chmod 600 ~/.ssh/id_rsa | |
| # Verify key format | |
| if ! grep -q "BEGIN.*PRIVATE KEY" ~/.ssh/id_rsa; then | |
| echo "Error: SSH key does not appear to be in correct format" | |
| exit 1 | |
| fi | |
| # Configure SSH to use only the key file and disable other auth methods | |
| cat > ~/.ssh/config <<EOF | |
| Host * | |
| IdentitiesOnly yes | |
| PreferredAuthentications publickey | |
| StrictHostKeyChecking no | |
| UserKnownHostsFile ~/.ssh/known_hosts | |
| PubkeyAuthentication yes | |
| PasswordAuthentication no | |
| ChallengeResponseAuthentication no | |
| GSSAPIAuthentication no | |
| GSSAPIKeyExchange no | |
| GSSAPIDelegateCredentials no | |
| Host $GREENGEEKS_HOST | |
| User $GREENGEEKS_USER | |
| IdentityFile ~/.ssh/id_rsa | |
| IdentitiesOnly yes | |
| PreferredAuthentications publickey | |
| PubkeyAuthentication yes | |
| PasswordAuthentication no | |
| ChallengeResponseAuthentication no | |
| GSSAPIAuthentication no | |
| GSSAPIKeyExchange no | |
| GSSAPIDelegateCredentials no | |
| NumberOfPasswordPrompts 0 | |
| EOF | |
| chmod 600 ~/.ssh/config | |
| # Disable SSH agent completely | |
| unset SSH_AUTH_SOCK | |
| unset SSH_AGENT_PID | |
| # Remove any default SSH keys that might interfere | |
| rm -f ~/.ssh/id_ed25519 ~/.ssh/id_ecdsa ~/.ssh/id_dsa ~/.ssh/id_rsa.pub 2>/dev/null | |
| ssh-keyscan -H "$GREENGEEKS_HOST" >> ~/.ssh/known_hosts 2>/dev/null | |
| - name: Download release assets | |
| env: | |
| SCP_HOST: ${{ vars.GREENGEEKS_SSH_HOST }} | |
| run: | | |
| flox activate -d flox/base -- ./gradlew :app:assetsDownloadRelease --no-daemon \ | |
| -Dorg.gradle.jvmargs="-Xmx10g -XX:MaxMetaspaceSize=2g -XX:+HeapDumpOnOutOfMemoryError --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED" \ | |
| -Dandroid.aapt2.daemonHeapSize=4096M \ | |
| -Dorg.gradle.workers.max=1 \ | |
| -Dorg.gradle.parallel=false | |
| - name: Upload release assets | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: assets | |
| path: assets/release | |
| retention-days: 1 | |
| - name: Cleanup google-services.json | |
| if: always() | |
| run: | | |
| rm -f app/google-services.json | |
| echo "google-services.json cleaned up successfully" | |
| - name: Cleanup ssh | |
| if: always() | |
| run: | | |
| # Remove SSH key | |
| rm -f ~/.ssh/id_rsa | |
| # Clean up SSH known_hosts (remove the entry for this host) | |
| SSH_HOST="${{ vars.GREENGEEKS_SSH_HOST }}" | |
| if [ -n "$SSH_HOST" ]; then | |
| ssh-keygen -R "$SSH_HOST" 2>/dev/null || true | |
| fi | |
| # Remove entire .ssh directory if empty | |
| rmdir ~/.ssh 2>/dev/null || true | |
| download_documentation: | |
| name: Download pre-compressed documentation DB | |
| runs-on: self-hosted | |
| timeout-minutes: 5 | |
| needs: download_assets | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: main | |
| - name: Authenticate to Google Cloud for Drive access | |
| id: auth_drive | |
| uses: google-github-actions/auth@v2 | |
| with: | |
| workload_identity_provider: ${{ secrets.WIF_PROVIDER }} | |
| service_account: ${{ secrets.IDENTITY_EMAIL }} | |
| token_format: 'access_token' | |
| access_token_scopes: 'https://www.googleapis.com/auth/drive.readonly' | |
| - name: Download pre-compressed documentation.db.br from Google Drive | |
| run: | | |
| FILE_ID="${{ secrets.DOCUMENTATION_BR_FILE_ID }}" | |
| ACCESS_TOKEN="${{ steps.auth_drive.outputs.access_token }}" | |
| if [ -z "$FILE_ID" ]; then | |
| echo "ERROR: DOCUMENTATION_BR_FILE_ID secret not set" | |
| exit 1 | |
| fi | |
| echo "Downloading documentation.db.br from Google Drive..." | |
| mkdir -p assets/release/common/database | |
| curl -sL -H "Authorization: Bearer $ACCESS_TOKEN" \ | |
| "https://www.googleapis.com/drive/v3/files/${FILE_ID}?alt=media&supportsAllDrives=true&acknowledgeAbuse=true" \ | |
| -o assets/release/common/database/documentation.db.br | |
| if [ ! -f assets/release/common/database/documentation.db.br ]; then | |
| echo "ERROR: Failed to download documentation.db.br" | |
| exit 1 | |
| fi | |
| BR_MD5_FILE_ID="${{ secrets.DOCUMENTATION_BR_MD5_FILE_ID }}" | |
| if [ -n "$BR_MD5_FILE_ID" ]; then | |
| echo "Verifying MD5 checksum..." | |
| EXPECTED_MD5=$(curl -s -H "Authorization: Bearer $ACCESS_TOKEN" \ | |
| "https://www.googleapis.com/drive/v3/files/${BR_MD5_FILE_ID}?alt=media" \ | |
| | tr -d '\n') | |
| ACTUAL_MD5=$(md5sum assets/release/common/database/documentation.db.br | awk '{print $1}') | |
| echo "Expected MD5: $EXPECTED_MD5" | |
| echo "Actual MD5: $ACTUAL_MD5" | |
| if [ "$ACTUAL_MD5" != "$EXPECTED_MD5" ]; then | |
| echo "ERROR: MD5 checksum mismatch" | |
| jq -n \ | |
| --arg expected "$EXPECTED_MD5" \ | |
| --arg actual "$ACTUAL_MD5" \ | |
| '{ | |
| blocks: [ | |
| { | |
| type: "header", | |
| text: { | |
| type: "plain_text", | |
| text: ":x: Release Build Failed - Documentation DB Integrity Check Failed", | |
| emoji: true | |
| } | |
| }, | |
| { | |
| type: "section", | |
| text: { | |
| type: "mrkdwn", | |
| text: "@here The daily release build failed because *documentation.db.br* failed integrity verification." | |
| } | |
| }, | |
| { | |
| type: "section", | |
| text: { | |
| type: "mrkdwn", | |
| text: "*Checksum Mismatch:*\n• Expected: `\($expected)`\n• Actual: `\($actual)`" | |
| } | |
| }, | |
| { | |
| type: "section", | |
| text: { | |
| type: "mrkdwn", | |
| text: "*Possible Causes:*\n• File was corrupted during download\n• File on Google Drive was modified after compression\n• MD5 file on Drive is out of sync" | |
| } | |
| }, | |
| { | |
| type: "section", | |
| text: { | |
| type: "mrkdwn", | |
| text: "*Action Required:*\n1. Re-run the compress_docdb workflow to regenerate the file and MD5\n2. Verify both files uploaded successfully to Google Drive" | |
| } | |
| } | |
| ] | |
| }' > error_payload.json | |
| curl -X POST -H "Content-type: application/json" --data @error_payload.json "${{ secrets.SLACK_WEBHOOK_URL }}" | |
| rm -f error_payload.json | |
| exit 1 | |
| fi | |
| echo "MD5 checksum verified successfully" | |
| fi | |
| FILE_SIZE_BYTES=$(stat -c%s assets/release/common/database/documentation.db.br 2>/dev/null || stat -f%z assets/release/common/database/documentation.db.br 2>/dev/null) | |
| FILE_SIZE_HUMAN=$(du -h assets/release/common/database/documentation.db.br | cut -f1) | |
| if [ "$FILE_SIZE_BYTES" -lt 100000 ]; then | |
| echo "ERROR: Downloaded file is too small ($FILE_SIZE_HUMAN)" | |
| jq -n '{ | |
| blocks: [ | |
| { | |
| type: "header", | |
| text: { | |
| type: "plain_text", | |
| text: ":x: Release Build Failed - Documentation DB Download Error", | |
| emoji: true | |
| } | |
| }, | |
| { | |
| type: "section", | |
| text: { | |
| type: "mrkdwn", | |
| text: "@here The daily release build failed because *documentation.db.br* could not be downloaded from Google Drive." | |
| } | |
| }, | |
| { | |
| type: "section", | |
| text: { | |
| type: "mrkdwn", | |
| text: "*Possible Causes:*\n• compress_docdb workflow has not run yet\n• File not found or incorrect file ID\n• Service account does not have access to the file" | |
| } | |
| }, | |
| { | |
| type: "section", | |
| text: { | |
| type: "mrkdwn", | |
| text: "*Action Required:*\n1. Verify compress_docdb workflow ran successfully\n2. Check that DOCUMENTATION_BR_FILE_ID secret is correct\n3. Ensure service account has Viewer access to the file" | |
| } | |
| } | |
| ] | |
| }' > error_payload.json | |
| curl -X POST -H "Content-type: application/json" --data @error_payload.json "${{ secrets.SLACK_WEBHOOK_URL }}" | |
| rm -f error_payload.json | |
| exit 1 | |
| fi | |
| echo "Successfully downloaded documentation.db.br ($FILE_SIZE_HUMAN)" | |
| - name: Upload compressed documentation DB | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: assets-db | |
| path: assets/release/common/database/documentation.db.br | |
| retention-days: 1 | |
| build_apk: | |
| name: Build Release APK | |
| runs-on: self-hosted | |
| timeout-minutes: 60 | |
| needs: [merge_stage_to_main, download_assets, download_documentation] | |
| strategy: | |
| matrix: | |
| include: | |
| - variant: v8 | |
| build_type: "RELEASE 64-bit" | |
| - variant: v7 | |
| build_type: "RELEASE 32-bit" | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| ref: main | |
| - name: Set branch to main for build | |
| id: determine_branch | |
| run: | | |
| echo "BRANCH_TO_CHECKOUT=main" >> $GITHUB_OUTPUT | |
| - name: Download release assets | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: assets | |
| path: assets/release | |
| - name: Download compressed documentation DB | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: assets-db | |
| path: assets/release/common/database | |
| - name: Initialize submodules | |
| run: | | |
| git submodule init | |
| git submodule update --remote | |
| - name: Check if Nix is installed | |
| id: check_nix | |
| run: | | |
| if command -v nix >/dev/null 2>&1; then | |
| echo "nix is installed" | |
| echo "nix_installed=true" >> $GITHUB_ENV | |
| else | |
| echo "nix is not installed" | |
| echo "nix_installed=false" >> $GITHUB_ENV | |
| fi | |
| - name: Install Flox | |
| if: env.nix_installed == 'false' | |
| uses: flox/install-flox-action@v2 | |
| - name: Create google-services.json | |
| env: | |
| GOOGLE_SERVICES_JSON: ${{ secrets.GOOGLE_SERVICES_JSON }} | |
| run: | | |
| echo "$GOOGLE_SERVICES_JSON" > app/google-services.json | |
| echo "google-services.json created successfully" | |
| - name: Assemble Release APK | |
| run: | | |
| variant_upper=$(echo "${{ matrix.variant }}" | tr '[:lower:]' '[:upper:]') | |
| flox activate -d flox/base -- ./gradlew :app:assemble${variant_upper}Release --no-daemon \ | |
| -Dorg.gradle.jvmargs="-Xmx10g -XX:MaxMetaspaceSize=2g -XX:+HeapDumpOnOutOfMemoryError --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED" \ | |
| -Dandroid.aapt2.daemonHeapSize=4096M \ | |
| -Dorg.gradle.workers.max=1 \ | |
| -Dorg.gradle.parallel=false | |
| - name: Find APK file | |
| id: find_apk | |
| run: | | |
| apk_path=$(find app/build/outputs/apk/ -path "*${{ matrix.variant }}*/release/*.apk" | head -n 1) | |
| echo "APK_PATH=$apk_path" >> $GITHUB_OUTPUT | |
| - name: Set branch name and build type | |
| run: | | |
| BRANCH_NAME=${{ steps.determine_branch.outputs.BRANCH_TO_CHECKOUT }} | |
| echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV | |
| BUILD_TYPE="${{ matrix.build_type }}" | |
| echo "BUILD_TYPE=$BUILD_TYPE" >> $GITHUB_ENV | |
| - name: Get Commit Information | |
| id: commit_info | |
| run: | | |
| COMMIT_MSG=$(git log -1 --pretty=%B | head -1 | tr -d '\n\r' | sed 's/[*]/•/g') | |
| COMMIT_AUTHOR=$(git log -1 --pretty=%an) | |
| echo "COMMIT_MSG=$COMMIT_MSG" >> $GITHUB_OUTPUT | |
| echo "COMMIT_AUTHOR=$COMMIT_AUTHOR" >> $GITHUB_OUTPUT | |
| - name: Authenticate to Google Cloud via Workload Identity | |
| uses: google-github-actions/auth@v2 | |
| with: | |
| workload_identity_provider: ${{ secrets.WIF_PROVIDER }} | |
| service_account: ${{ secrets.IDENTITY_EMAIL }} | |
| - name: Verify APK exists | |
| run: | | |
| if [ ! -f "${{ steps.find_apk.outputs.APK_PATH }}" ]; then | |
| echo "ERROR: APK file not found at ${{ steps.find_apk.outputs.APK_PATH }}" | |
| exit 1 | |
| fi | |
| ls -la "${{ steps.find_apk.outputs.APK_PATH }}" | |
| - name: Deploy to Firebase App Distribution | |
| id: firebase_upload | |
| env: | |
| APK_PATH: ${{ steps.find_apk.outputs.APK_PATH }} | |
| FIREBASE_APP_ID: ${{ secrets.FIREBASE_APP_ID }} | |
| BUILD_TYPE: ${{ matrix.build_type }} | |
| COMMIT_MSG: ${{ steps.commit_info.outputs.COMMIT_MSG }} | |
| run: | | |
| echo "Starting Firebase deployment..." | |
| echo "APK Path: $APK_PATH" | |
| echo "Firebase App ID: $FIREBASE_APP_ID" | |
| # Check if Firebase CLI is authenticated | |
| echo "Checking Firebase authentication..." | |
| firebase projects:list || { | |
| echo "ERROR: Firebase authentication failed" | |
| exit 1 | |
| } | |
| RELEASE_NOTES_FILE=$(mktemp) | |
| cat > "$RELEASE_NOTES_FILE" << EOF | |
| Build Type: $BUILD_TYPE | |
| Commit: $COMMIT_MSG | |
| EOF | |
| echo "Running Firebase distribution command..." | |
| set +e # Disable exit on error temporarily | |
| output=$(firebase appdistribution:distribute "$APK_PATH" \ | |
| --app "$FIREBASE_APP_ID" \ | |
| --groups "testers" \ | |
| --release-notes-file "$RELEASE_NOTES_FILE" 2>&1) | |
| exit_code=$? | |
| set -e # Re-enable exit on error | |
| echo "Firebase command exit code: $exit_code" | |
| echo "Firebase command output:" | |
| echo "$output" | |
| if [ $exit_code -ne 0 ]; then | |
| echo "ERROR: Firebase deployment failed with exit code $exit_code" | |
| exit 1 | |
| fi | |
| FIREBASE_URL=$(echo "$output" | grep -oE 'https://console\.firebase\.google\.com/project/[^/]+/appdistribution/app/[^/]+/releases/[^?]+(\?[^"]*)?') || FIREBASE_URL="" | |
| if [ -z "$FIREBASE_URL" ]; then | |
| FIREBASE_URL="${{ env.FIREBASE_CONSOLE_URL }}" | |
| fi | |
| echo "FIREBASE_CONSOLE_URL=$FIREBASE_URL" >> $GITHUB_OUTPUT | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@v6 | |
| - name: Upload APK to Cloudflare R2 | |
| env: | |
| CLOUDFLARE_ACCOUNT_ID: ${{ vars.CLOUDFLARE_ACCOUNT_ID }} | |
| CLOUDFLARE_KEY_ID: ${{ vars.CLOUDFLARE_KEY_ID }} | |
| CLOUDFLARE_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_SECRET_ACCESS_KEY }} | |
| run: | | |
| uv run --with boto3 scripts/cloudflare-r2-upload.py "${{ steps.find_apk.outputs.APK_PATH }}" | |
| - name: Clean up build folder after upload | |
| run: | | |
| echo "Cleaning up build folder after Firebase upload..." | |
| rm -rf app/build/ | |
| echo "Build folder cleanup completed" | |
| - name: Install jq | |
| run: sudo apt-get update && sudo apt-get install -y jq | |
| - name: Send Rich Slack Notification | |
| env: | |
| SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }} | |
| COMMIT_AUTHOR: ${{ steps.commit_info.outputs.COMMIT_AUTHOR }} | |
| COMMIT_MSG: ${{ steps.commit_info.outputs.COMMIT_MSG }} | |
| FIREBASE_CONSOLE_URL: ${{ steps.firebase_upload.outputs.FIREBASE_CONSOLE_URL }} | |
| run: | | |
| BRANCH_NAME="${{ env.BRANCH_NAME }}" | |
| BUILD_TYPE="${{ matrix.build_type }}" | |
| jq -n \ | |
| --arg commit_msg "$COMMIT_MSG" \ | |
| --arg build_type "$BUILD_TYPE" \ | |
| --arg commit_author "$COMMIT_AUTHOR" \ | |
| --arg firebase_url "$FIREBASE_CONSOLE_URL" \ | |
| --arg branch_name "$BRANCH_NAME" \ | |
| '{ | |
| blocks: [ | |
| { | |
| type: "header", | |
| text: { | |
| type: "plain_text", | |
| text: ":rocket: [Release] New Build Available", | |
| emoji: true | |
| } | |
| }, | |
| { | |
| type: "section", | |
| text: { | |
| type: "mrkdwn", | |
| text: "@here Please review and test this build." | |
| } | |
| }, | |
| { | |
| type: "section", | |
| text: { | |
| type: "mrkdwn", | |
| text: "*Build Type:* \($build_type)" | |
| } | |
| }, | |
| { | |
| type: "section", | |
| text: { | |
| type: "mrkdwn", | |
| text: "*Commit:* \($commit_msg)" | |
| } | |
| }, | |
| { | |
| type: "section", | |
| text: { | |
| type: "mrkdwn", | |
| text: "*Author:* @\($commit_author)" | |
| } | |
| }, | |
| { | |
| type: "actions", | |
| elements: [ | |
| { | |
| type: "button", | |
| text: { | |
| type: "plain_text", | |
| text: "View on Firebase", | |
| emoji: true | |
| }, | |
| url: $firebase_url, | |
| action_id: "firebase-console" | |
| } | |
| ] | |
| }, | |
| { | |
| type: "divider" | |
| }, | |
| { | |
| type: "context", | |
| elements: [ | |
| { | |
| type: "mrkdwn", | |
| text: "Deployed from branch `\($branch_name)`" | |
| } | |
| ] | |
| } | |
| ] | |
| }' > payload.json | |
| curl -X POST -H "Content-type: application/json" --data @payload.json "$SLACK_WEBHOOK" | |
| rm -f payload.json | |
| - name: Send Telegram message | |
| env: | |
| TELEGRAM_TOKEN: ${{ secrets.TELEGRAM_TOKEN }} | |
| TELEGRAM_EARLY_ACCESS_CHAT_ID: ${{ vars.TELEGRAM_EARLY_ACCESS_CHAT_ID }} | |
| APK_PATH: ${{ steps.find_apk.outputs.APK_PATH }} | |
| run: | | |
| GIT_LOG=$(git log --oneline --since "24 hours ago" || true) | |
| if [ -z "$GIT_LOG" ]; then | |
| GIT_LOG="(no commits in the last 24 hours)" | |
| fi | |
| APK_FILENAME=$(basename "$APK_PATH") | |
| DOWNLOAD_URL="https://download.appdevforall.org/${APK_FILENAME}" | |
| MESSAGE=$(printf "Download: %s\n\n%s" "$DOWNLOAD_URL" "$GIT_LOG") | |
| MESSAGE="${MESSAGE:0:4096}" | |
| curl -s -X POST -H "Content-Type: application/json" \ | |
| -d "$(jq -n --arg chat_id "$TELEGRAM_EARLY_ACCESS_CHAT_ID" --arg text "$MESSAGE" '{chat_id: $chat_id, text: $text}')" \ | |
| "https://api.telegram.org/bot${TELEGRAM_TOKEN}/sendMessage" | |
| - name: Cleanup google-services.json | |
| if: always() | |
| run: | | |
| rm -f app/google-services.json | |
| echo "google-services.json cleaned up successfully" |