Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 180 additions & 0 deletions .github/workflows/playstore-deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
name: Deploy to Play Store (Internal Testing)
permissions:
contents: read

# ─────────────────────────────────────────────────────────
# Trigger: manual dispatch only (mirrors testflight-deploy.yml).
# To automate on push-to-main, add:
# push:
# branches: [ main ]
# ─────────────────────────────────────────────────────────

on:
push:
branches:
- android_action
workflow_dispatch:

env:
# Android package name (must match android/app/build.gradle applicationId)
PACKAGE_NAME: com.mieweb.pulse
# Java version required by the React Native / Expo Gradle plugin
JAVA_VERSION: "17"
NODE_VERSION: "20"

jobs:
deploy-android:
name: Build & Deploy Android to Play Store Internal Track
runs-on: ubuntu-latest

steps:
# ── 1. Source Code ────────────────────────────────────────────────
- name: Checkout code
uses: actions/checkout@v4

# ── 2. Node.js ────────────────────────────────────────────────────
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: "npm"

# ── 3. Java (Gradle requires JDK 17 for modern AGP) ───────────────
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: "temurin"
java-version: ${{ env.JAVA_VERSION }}
cache: "gradle"

# ── 4. JS Dependencies ────────────────────────────────────────────
- name: Install Node dependencies
run: npm install

# ── 5. Expo CLI (needed for prebuild + Metro bundler) ─────────────
- name: Install Expo CLI
run: npm install -g @expo/cli

# ── 6. Version ────────────────────────────────────────────────────
# Read the version string from app.json so it appears in the build
# summary without hard-coding it here.
- name: Read version from app.json
run: |
VERSION=$(node -p "require('./app.json').expo.version")
echo "VERSION=$VERSION" >> $GITHUB_ENV

# ── 7. Expo Prebuild ──────────────────────────────────────────────
# Regenerates the android/ directory from app.json + Expo plugins
# for a clean, reproducible build (same approach as testflight-deploy.yml).
# Remove this step if you maintain manual native changes in android/.
- name: Prebuild Android project
run: npx expo prebuild --platform android --clean

# ── 8. Decode Keystore ────────────────────────────────────────────
# The release keystore is stored as a base64-encoded GitHub Secret.
# We decode it into the android/app/ directory where Gradle expects it.
- name: Decode release keystore
run: |
echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 --decode \
> android/app/pulse-release.jks

# ── 9. Write keystore.properties ──────────────────────────────────
# android/app/build.gradle reads signing credentials from this file.
# Written here from GitHub Secrets — never committed to the repo.
- name: Write keystore.properties
run: |
cat > android/keystore.properties << EOF
storeFile=app/pulse-release.jks
storePassword=${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
keyAlias=${{ secrets.ANDROID_KEY_ALIAS }}
keyPassword=${{ secrets.ANDROID_KEY_PASSWORD }}
EOF

# ── 10. Gradle Cache ──────────────────────────────────────────────
# Cache the Gradle wrapper + caches to speed up subsequent builds.
- name: Cache Gradle files
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: gradle-${{ runner.os }}-${{ hashFiles('android/gradle/wrapper/gradle-wrapper.properties') }}
restore-keys: |
gradle-${{ runner.os }}-

# ── 11. Permissions ───────────────────────────────────────────────
- name: Make Gradle wrapper executable
run: chmod +x android/gradlew

# ── 12. Build Release AAB ─────────────────────────────────────────
# Produces a signed Android App Bundle under android/app/build/outputs/bundle/.
# The signing config is read from android/keystore.properties (step 9).
- name: Build release AAB
run: cd android && ./gradlew bundleRelease --no-daemon

# ── 13. Locate generated AAB ─────────────────────────────────────
# After bundleRelease, Gradle may place the AAB under a flavor-specific
# sub-directory (e.g. release/ or prodRelease/). Discover it
# dynamically so the path is correct regardless of variant naming
# introduced by Expo or custom Gradle flavors.
- name: Locate generated AAB
run: |
AAB=$(find android/app/build/outputs/bundle -maxdepth 3 -name "*.aab" -type f | head -n 1)
if [ -z "$AAB" ]; then
echo "ERROR: No .aab file found under android/app/build/outputs/bundle"
echo "Contents of android/app/build/outputs (if any):"
find android/app/build/outputs -type f 2>/dev/null || echo " (no files found)"
exit 1
fi
echo "Found AAB: $AAB ($(ls -lh "$AAB" | awk '{print $5}'))"
echo "AAB_PATH=$AAB" >> "$GITHUB_ENV"

# ── 14. Verify AAB ────────────────────────────────────────────────
# Confirm the resolved path is a readable file before attempting upload.
- name: Verify AAB exists
run: |
echo "AAB path: ${{ env.AAB_PATH }}"
if [ ! -f "${{ env.AAB_PATH }}" ]; then
echo "ERROR: AAB not found at ${{ env.AAB_PATH }}"
find android/app/build/outputs -type f 2>/dev/null || echo "No outputs found"
exit 1
fi
echo "AAB verified: $(ls -lh "${{ env.AAB_PATH }}")"

# ── 15. Upload to Google Play — Internal Testing ──────────────────
# Uses the official r0adkll/upload-google-play action.
# serviceAccountJsonPlainText: full JSON content of the service account key.
# track: internal → uploads to the Internal Testing track.
- name: Upload AAB to Google Play Internal Testing
uses: r0adkll/upload-google-play@v1
with:
serviceAccountJsonPlainText: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON }}
packageName: ${{ env.PACKAGE_NAME }}
releaseFiles: ${{ env.AAB_PATH }}
track: internal
status: completed
releaseName: "Pulse v${{ env.VERSION }}"

# ── 16. Archive AAB as Workflow Artifact ──────────────────────────
# Keeps the signed AAB accessible from the Actions run for 30 days.
- name: Upload AAB as artifact
if: always()
uses: actions/upload-artifact@v4
with:
name: pulse-release-${{ env.VERSION }}.aab
path: ${{ env.AAB_PATH }}
retention-days: 30

# ── 17. Build Summary ─────────────────────────────────────────────
- name: Deployment summary
run: |
echo "## Deploy to Play Store" >> $GITHUB_STEP_SUMMARY
echo "- **Version**: ${{ env.VERSION }}" >> $GITHUB_STEP_SUMMARY
echo "- **Package**: ${{ env.PACKAGE_NAME }}" >> $GITHUB_STEP_SUMMARY
echo "- **Track**: Internal Testing" >> $GITHUB_STEP_SUMMARY
echo "- **AAB**: \`${{ env.AAB_PATH }}\`" >> $GITHUB_STEP_SUMMARY

# ── 18. Failure Notice ────────────────────────────────────────────
- name: Notify on failure
if: failure()
run: echo "## Android Deployment Failed" >> $GITHUB_STEP_SUMMARY
Loading