diff --git a/.github/scripts/attach-apk-to-release.sh b/.github/scripts/attach-apk-to-release.sh new file mode 100755 index 00000000..174023aa --- /dev/null +++ b/.github/scripts/attach-apk-to-release.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +# ============================================================================= +# Attach APK to GitHub Release +# ============================================================================= +# This script attaches a built APK to a GitHub Release with proper naming +# +# Usage: +# ./attach-apk-to-release.sh +# +# Arguments: +# tag_name - Git tag name (e.g., krow-withus-worker-mobile/dev-v0.1.0) +# app - worker-mobile-app or client-mobile-app +# app_name - staff or client (internal build folder name) +# version - Version number (e.g., 0.1.0) +# environment - dev, stage, or prod +# +# Environment Variables: +# GH_TOKEN - GitHub token for gh CLI authentication +# ============================================================================= + +set -e + +TAG_NAME="$1" +APP="$2" +APP_NAME="$3" +VERSION="$4" +ENV="$5" + +if [ -z "$TAG_NAME" ] || [ -z "$APP" ] || [ -z "$APP_NAME" ] || [ -z "$VERSION" ] || [ -z "$ENV" ]; then + echo "❌ Error: Missing required arguments" + echo "Usage: $0 " + exit 1 +fi + +# Find APK in build output +APK_PATH="apps/mobile/apps/${APP_NAME}/build/app/outputs/flutter-apk/app-release.apk" + +if [ ! -f "$APK_PATH" ]; then + echo "❌ Error: APK not found at $APK_PATH" + echo "Searching for APK files..." + find apps/mobile/apps/${APP_NAME} -name "*.apk" + exit 1 +fi + +# Create proper APK name based on app type +if [ "$APP" = "worker-mobile-app" ]; then + APK_NAME="krow-withus-worker-mobile-${ENV}-v${VERSION}.apk" +else + APK_NAME="krow-withus-client-mobile-${ENV}-v${VERSION}.apk" +fi + +# Copy APK with proper name +cp "$APK_PATH" "/tmp/$APK_NAME" + +# Upload to GitHub Release +echo "📤 Uploading $APK_NAME to release $TAG_NAME..." +gh release upload "$TAG_NAME" "/tmp/$APK_NAME" --clobber + +echo "✅ APK attached to release: $APK_NAME" diff --git a/.github/scripts/setup-apk-signing.sh b/.github/scripts/setup-apk-signing.sh new file mode 100755 index 00000000..fe982f6a --- /dev/null +++ b/.github/scripts/setup-apk-signing.sh @@ -0,0 +1,102 @@ +#!/bin/bash + +# ============================================================================= +# Setup APK Signing for GitHub Actions +# ============================================================================= +# This script configures Android APK signing by decoding keystores from +# GitHub Secrets and setting up environment variables for build.gradle.kts +# +# Usage: +# ./setup-apk-signing.sh +# +# Arguments: +# app - worker-mobile-app or client-mobile-app +# environment - dev, stage, or prod +# temp_dir - Temporary directory for keystore files (e.g., ${{ runner.temp }}) +# +# Environment Variables (must be set): +# WORKER_KEYSTORE_DEV_BASE64, WORKER_KEYSTORE_STAGING_BASE64, WORKER_KEYSTORE_PROD_BASE64 +# WORKER_KEYSTORE_PASSWORD_DEV, WORKER_KEYSTORE_PASSWORD_STAGING, WORKER_KEYSTORE_PASSWORD_PROD +# WORKER_KEY_ALIAS_DEV, WORKER_KEY_ALIAS_STAGING, WORKER_KEY_ALIAS_PROD +# WORKER_KEY_PASSWORD_DEV, WORKER_KEY_PASSWORD_STAGING, WORKER_KEY_PASSWORD_PROD +# CLIENT_KEYSTORE_DEV_BASE64, CLIENT_KEYSTORE_STAGING_BASE64, CLIENT_KEYSTORE_PROD_BASE64 +# CLIENT_KEYSTORE_PASSWORD_DEV, CLIENT_KEYSTORE_PASSWORD_STAGING, CLIENT_KEYSTORE_PASSWORD_PROD +# CLIENT_KEY_ALIAS_DEV, CLIENT_KEY_ALIAS_STAGING, CLIENT_KEY_ALIAS_PROD +# CLIENT_KEY_PASSWORD_DEV, CLIENT_KEY_PASSWORD_STAGING, CLIENT_KEY_PASSWORD_PROD +# ============================================================================= + +set -e + +APP="$1" +ENV="$2" +TEMP_DIR="$3" + +if [ -z "$APP" ] || [ -z "$ENV" ] || [ -z "$TEMP_DIR" ]; then + echo "❌ Error: Missing required arguments" + echo "Usage: $0 " + exit 1 +fi + +echo "🔐 Setting up Android signing for $APP in $ENV environment..." + +# Determine which keystore to use +if [ "$APP" = "worker-mobile-app" ]; then + APP_TYPE="WORKER" + APP_NAME="STAFF" # CodeMagic uses STAFF in env var names +else + APP_TYPE="CLIENT" + APP_NAME="CLIENT" +fi + +# Convert environment to uppercase for env var names +ENV_UPPER=$(echo "$ENV" | tr '[:lower:]' '[:upper:]') +if [ "$ENV_UPPER" = "STAGE" ]; then + ENV_UPPER="STAGING" # CodeMagic uses STAGING instead of STAGE +fi + +# Get the keystore secret name dynamically +KEYSTORE_BASE64_VAR="${APP_TYPE}_KEYSTORE_${ENV_UPPER}_BASE64" +KEYSTORE_PASSWORD_VAR="${APP_TYPE}_KEYSTORE_PASSWORD_${ENV_UPPER}" +KEY_ALIAS_VAR="${APP_TYPE}_KEY_ALIAS_${ENV_UPPER}" +KEY_PASSWORD_VAR="${APP_TYPE}_KEY_PASSWORD_${ENV_UPPER}" + +# Get values using indirect expansion +KEYSTORE_BASE64="${!KEYSTORE_BASE64_VAR}" +KEYSTORE_PASSWORD="${!KEYSTORE_PASSWORD_VAR}" +KEY_ALIAS="${!KEY_ALIAS_VAR}" +KEY_PASSWORD="${!KEY_PASSWORD_VAR}" + +# Check if secrets are configured +if [ -z "$KEYSTORE_BASE64" ]; then + echo "âš ī¸ WARNING: Keystore secret $KEYSTORE_BASE64_VAR is not configured!" + echo "âš ī¸ APK will be built UNSIGNED for $ENV environment." + echo "âš ī¸ Please configure GitHub Secrets as documented in docs/RELEASE/APK_SIGNING_SETUP.md" + exit 0 +fi + +# Create temporary directory for keystore +KEYSTORE_DIR="${TEMP_DIR}/keystores" +mkdir -p "$KEYSTORE_DIR" +KEYSTORE_PATH="$KEYSTORE_DIR/release.jks" + +# Decode keystore from base64 +echo "$KEYSTORE_BASE64" | base64 -d > "$KEYSTORE_PATH" + +if [ ! -f "$KEYSTORE_PATH" ]; then + echo "❌ Failed to decode keystore!" + exit 1 +fi + +echo "✅ Keystore decoded successfully" +echo "đŸ“Ļ Keystore size: $(ls -lh "$KEYSTORE_PATH" | awk '{print $5}')" + +# Export environment variables for build.gradle.kts +# Using CodeMagic-compatible variable names +echo "CI=true" >> $GITHUB_ENV +echo "CM_KEYSTORE_PATH_${APP_NAME}=$KEYSTORE_PATH" >> $GITHUB_ENV +echo "CM_KEYSTORE_PASSWORD_${APP_NAME}=$KEYSTORE_PASSWORD" >> $GITHUB_ENV +echo "CM_KEY_ALIAS_${APP_NAME}=$KEY_ALIAS" >> $GITHUB_ENV +echo "CM_KEY_PASSWORD_${APP_NAME}=$KEY_PASSWORD" >> $GITHUB_ENV + +echo "✅ Signing environment configured for $APP_NAME ($ENV environment)" +echo "🔑 Using key alias: $KEY_ALIAS" diff --git a/.github/scripts/setup-mobile-github-secrets.sh b/.github/scripts/setup-mobile-github-secrets.sh new file mode 100755 index 00000000..3645bb82 --- /dev/null +++ b/.github/scripts/setup-mobile-github-secrets.sh @@ -0,0 +1,262 @@ +#!/bin/bash + +# ============================================================================= +# GitHub Secrets Setup Helper +# ============================================================================= +# This script helps you configure GitHub Secrets for APK signing +# +# Usage: +# ./setup-mobile-github-secrets.sh +# +# Reference: docs/RELEASE/APK_SIGNING_SETUP.md +# ============================================================================= + +set -e + +REPO_ROOT=$(git rev-parse --show-toplevel) +cd "$REPO_ROOT" + +echo "🔐 GitHub Secrets Setup Helper for APK Signing" +echo "================================================" +echo "" + +# Colors for output +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +RED='\033[0;31m' +NC='\033[0m' # No Color + +# Track successful secret generations +SECRETS_FOUND=0 +TOTAL_SECRETS=24 + +# ============================================================================= +# Helper Functions +# ============================================================================= + +print_secret_config() { + local app=$1 + local env=$2 + local keystore_path=$3 + local password=$4 + local alias=$5 + local key_password=$6 + + local app_upper=$(echo "$app" | tr '[:lower:]' '[:upper:]') + local env_upper=$(echo "$env" | tr '[:lower:]' '[:upper:]') + + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo " ${app_upper} Mobile - ${env_upper} Environment" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + + if [ -f "$keystore_path" ]; then + echo -e "${GREEN}✅ Keystore found:${NC} $keystore_path" + + # Show keystore info + echo "" + echo "📋 Keystore Information:" + keytool -list -v -keystore "$keystore_path" -storepass "$password" 2>/dev/null | head -n 15 || echo " (Use keytool to inspect)" + + # Generate base64 + echo "" + echo "đŸ“Ļ Base64 Encoded Keystore:" + echo "" + BASE64_OUTPUT=$(base64 -i "$keystore_path") + echo "$BASE64_OUTPUT" + echo "" + + echo "GitHub Secrets to create:" + echo "" + echo " ${app_upper}_KEYSTORE_${env_upper}_BASE64" + echo " ${app_upper}_KEYSTORE_PASSWORD_${env_upper} = $password" + echo " ${app_upper}_KEY_ALIAS_${env_upper} = $alias" + echo " ${app_upper}_KEY_PASSWORD_${env_upper} = $key_password" + echo "" + + # Increment success counter (4 secrets per keystore) + SECRETS_FOUND=$((SECRETS_FOUND + 4)) + + else + echo -e "${YELLOW}âš ī¸ Keystore not found:${NC} $keystore_path" + echo "" + echo "This keystore should be stored securely (CodeMagic or secure storage)." + echo "" + fi +} + +# ============================================================================= +# Worker Mobile (Staff App) +# ============================================================================= + +echo "" +echo "═══════════════════════════════════════════════════════" +echo " WORKER MOBILE (Staff App) Configuration" +echo "═══════════════════════════════════════════════════════" + +# DEV Environment +print_secret_config \ + "worker" \ + "dev" \ + "$REPO_ROOT/apps/mobile/apps/staff/android/app/krow_with_us_staff_dev.jks" \ + "krowwithus" \ + "krow_staff_dev" \ + "krowwithus" + +# STAGING Environment +print_secret_config \ + "worker" \ + "staging" \ + "$REPO_ROOT/keystores/krow_staff_staging.jks" \ + "YOUR_STAGING_PASSWORD" \ + "krow_staff_staging" \ + "YOUR_STAGING_KEY_PASSWORD" + +# PROD Environment +print_secret_config \ + "worker" \ + "prod" \ + "$REPO_ROOT/keystores/krow_staff_prod.jks" \ + "YOUR_PROD_PASSWORD" \ + "krow_staff_prod" \ + "YOUR_PROD_KEY_PASSWORD" + +# ============================================================================= +# Client Mobile +# ============================================================================= + +echo "" +echo "═══════════════════════════════════════════════════════" +echo " CLIENT MOBILE Configuration" +echo "═══════════════════════════════════════════════════════" + +# DEV Environment +print_secret_config \ + "client" \ + "dev" \ + "$REPO_ROOT/apps/mobile/apps/client/android/app/krow_with_us_client_dev.jks" \ + "krowwithus" \ + "krow_client_dev" \ + "krowwithus" + +# STAGING Environment +print_secret_config \ + "client" \ + "staging" \ + "$REPO_ROOT/keystores/krow_client_staging.jks" \ + "YOUR_STAGING_PASSWORD" \ + "krow_client_staging" \ + "YOUR_STAGING_KEY_PASSWORD" + +# PROD Environment +print_secret_config \ + "client" \ + "prod" \ + "$REPO_ROOT/keystores/krow_client_prod.jks" \ + "YOUR_PROD_PASSWORD" \ + "krow_client_prod" \ + "YOUR_PROD_KEY_PASSWORD" + +# ============================================================================= +# Summary +# ============================================================================= + +echo "" +echo "═══════════════════════════════════════════════════════" +echo " SUMMARY" +echo "═══════════════════════════════════════════════════════" +echo "" +echo "Total secrets needed: ${TOTAL_SECRETS}" +echo "Secrets successfully generated: ${SECRETS_FOUND}" +echo "" +echo " â€ĸ 6 keystores (base64 encoded)" +echo " â€ĸ 6 keystore passwords" +echo " â€ĸ 6 key aliases" +echo " â€ĸ 6 key passwords" +echo "" + +if [ $SECRETS_FOUND -gt 0 ]; then + echo "Generated secrets to add to GitHub:" + echo "" + + # Worker Dev Secrets + if [ -f "$REPO_ROOT/apps/mobile/apps/staff/android/app/krow_with_us_staff_dev.jks" ]; then + echo " ✅ WORKER_KEYSTORE_DEV_BASE64" + echo " $(base64 -i "$REPO_ROOT/apps/mobile/apps/staff/android/app/krow_with_us_staff_dev.jks")" + echo "" + echo " ✅ WORKER_KEYSTORE_PASSWORD_DEV" + echo " krowwithus" + echo "" + echo " ✅ WORKER_KEY_ALIAS_DEV" + echo " krow_staff_dev" + echo "" + echo " ✅ WORKER_KEY_PASSWORD_DEV" + echo " krowwithus" + echo "" + fi + + # Client Dev Secrets + if [ -f "$REPO_ROOT/apps/mobile/apps/client/android/app/krow_with_us_client_dev.jks" ]; then + echo " ✅ CLIENT_KEYSTORE_DEV_BASE64" + echo " $(base64 -i "$REPO_ROOT/apps/mobile/apps/client/android/app/krow_with_us_client_dev.jks")" + echo "" + echo " ✅ CLIENT_KEYSTORE_PASSWORD_DEV" + echo " krowwithus" + echo "" + echo " ✅ CLIENT_KEY_ALIAS_DEV" + echo " krow_client_dev" + echo "" + echo " ✅ CLIENT_KEY_PASSWORD_DEV" + echo " krowwithus" + echo "" + fi +fi + +if [ $SECRETS_FOUND -lt $TOTAL_SECRETS ]; then + echo "Missing secrets (keystores not found):" + echo "" + + if [ ! -f "$REPO_ROOT/keystores/krow_staff_staging.jks" ]; then + echo " âš ī¸ WORKER_KEYSTORE_STAGING_BASE64" + echo " âš ī¸ WORKER_KEYSTORE_PASSWORD_STAGING" + echo " âš ī¸ WORKER_KEY_ALIAS_STAGING" + echo " âš ī¸ WORKER_KEY_PASSWORD_STAGING" + fi + + if [ ! -f "$REPO_ROOT/keystores/krow_staff_prod.jks" ]; then + echo " âš ī¸ WORKER_KEYSTORE_PROD_BASE64" + echo " âš ī¸ WORKER_KEYSTORE_PASSWORD_PROD" + echo " âš ī¸ WORKER_KEY_ALIAS_PROD" + echo " âš ī¸ WORKER_KEY_PASSWORD_PROD" + fi + + if [ ! -f "$REPO_ROOT/keystores/krow_client_staging.jks" ]; then + echo " âš ī¸ CLIENT_KEYSTORE_STAGING_BASE64" + echo " âš ī¸ CLIENT_KEYSTORE_PASSWORD_STAGING" + echo " âš ī¸ CLIENT_KEY_ALIAS_STAGING" + echo " âš ī¸ CLIENT_KEY_PASSWORD_STAGING" + fi + + if [ ! -f "$REPO_ROOT/keystores/krow_client_prod.jks" ]; then + echo " âš ī¸ CLIENT_KEYSTORE_PROD_BASE64" + echo " âš ī¸ CLIENT_KEYSTORE_PASSWORD_PROD" + echo " âš ī¸ CLIENT_KEY_ALIAS_PROD" + echo " âš ī¸ CLIENT_KEY_PASSWORD_PROD" + fi + + echo "" + echo "Retrieve missing keystores from CodeMagic Team Settings or secure storage." +fi + +echo "" +echo "To configure GitHub Secrets:" +echo "" +echo " 1. Go to: https://github.com/Oloodi/krow-workforce/settings/secrets/actions" +echo " 2. Click 'New repository secret'" +echo " 3. Add each secret listed above" +echo "" +echo "For complete documentation, see:" +echo " docs/RELEASE/APK_SIGNING_SETUP.md" +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" diff --git a/.github/scripts/verify-apk-signature.sh b/.github/scripts/verify-apk-signature.sh new file mode 100755 index 00000000..16832d02 --- /dev/null +++ b/.github/scripts/verify-apk-signature.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +# ============================================================================= +# Verify APK Signature +# ============================================================================= +# This script verifies that an APK is properly signed and displays +# certificate information +# +# Usage: +# ./verify-apk-signature.sh +# +# Arguments: +# apk_path - Path to the APK file to verify +# ============================================================================= + +set -e + +APK_PATH="$1" + +if [ -z "$APK_PATH" ]; then + echo "❌ Error: Missing APK path" + echo "Usage: $0 " + exit 1 +fi + +if [ ! -f "$APK_PATH" ]; then + echo "❌ APK not found at: $APK_PATH" + exit 1 +fi + +echo "🔍 Verifying APK signature..." + +# Check if APK is signed +if jarsigner -verify -verbose "$APK_PATH" 2>&1 | grep -q "jar verified"; then + echo "✅ APK is properly signed!" + + # Extract certificate details + echo "" + echo "📜 Certificate Details:" + jarsigner -verify -verbose -certs "$APK_PATH" 2>&1 | grep -A 3 "X.509" || true + + # Get signer info + echo "" + echo "🔑 Signer Information:" + keytool -printcert -jarfile "$APK_PATH" | head -n 15 + +else + echo "âš ī¸ WARNING: APK signature verification failed or APK is unsigned!" + echo "" + echo "This may happen if:" + echo " 1. GitHub Secrets are not configured for this environment" + echo " 2. Keystore credentials are incorrect" + echo " 3. Build configuration didn't apply signing" + echo "" + echo "See: docs/RELEASE/APK_SIGNING_SETUP.md for setup instructions" + + # Don't fail the build, just warn + # exit 1 +fi diff --git a/.github/workflows/product-release.yml b/.github/workflows/product-release.yml index 966e405a..f3f3930c 100644 --- a/.github/workflows/product-release.yml +++ b/.github/workflows/product-release.yml @@ -35,6 +35,9 @@ jobs: runs-on: ubuntu-latest permissions: contents: write + outputs: + version: ${{ steps.version.outputs.version }} + tag_name: ${{ steps.tag.outputs.tag_name }} steps: - name: đŸ“Ĩ Checkout repository @@ -143,3 +146,146 @@ jobs: "${{ github.event.inputs.environment }}" \ "${{ steps.version.outputs.version }}" \ "${{ steps.tag.outputs.tag_name }}" + + build-mobile-artifacts: + name: 📱 Build Mobile APK + runs-on: ubuntu-latest + needs: validate-and-create-release + if: ${{ github.event.inputs.app == 'worker-mobile-app' || github.event.inputs.app == 'client-mobile-app' }} + permissions: + contents: write + + steps: + - name: đŸ“Ĩ Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: đŸŸĸ Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + cache-dependency-path: 'backend/*/package-lock.json' + + - name: đŸ”Ĩ Install Firebase CLI + run: | + npm install -g firebase-tools + firebase --version + echo "â„šī¸ Note: Firebase CLI installed for Data Connect SDK generation" + echo "â„šī¸ If SDK generation fails, ensure Data Connect SDK files are committed to repo" + + - name: ☕ Setup Java + uses: actions/setup-java@v4 + with: + distribution: 'zulu' + java-version: '17' + + - name: đŸĻ Setup Flutter + uses: subosito/flutter-action@v2 + with: + flutter-version: '3.24.5' + channel: 'stable' + cache: true + + - name: 🔧 Install Melos + run: | + dart pub global activate melos + echo "$HOME/.pub-cache/bin" >> $GITHUB_PATH + + - name: đŸ“Ļ Install Dependencies + run: | + make mobile-install + + - name: 🔐 Setup APK Signing + env: + # Worker Mobile (Staff App) Secrets + WORKER_KEYSTORE_DEV_BASE64: ${{ secrets.WORKER_KEYSTORE_DEV_BASE64 }} + WORKER_KEYSTORE_STAGING_BASE64: ${{ secrets.WORKER_KEYSTORE_STAGING_BASE64 }} + WORKER_KEYSTORE_PROD_BASE64: ${{ secrets.WORKER_KEYSTORE_PROD_BASE64 }} + WORKER_KEYSTORE_PASSWORD_DEV: ${{ secrets.WORKER_KEYSTORE_PASSWORD_DEV }} + WORKER_KEYSTORE_PASSWORD_STAGING: ${{ secrets.WORKER_KEYSTORE_PASSWORD_STAGING }} + WORKER_KEYSTORE_PASSWORD_PROD: ${{ secrets.WORKER_KEYSTORE_PASSWORD_PROD }} + WORKER_KEY_ALIAS_DEV: ${{ secrets.WORKER_KEY_ALIAS_DEV }} + WORKER_KEY_ALIAS_STAGING: ${{ secrets.WORKER_KEY_ALIAS_STAGING }} + WORKER_KEY_ALIAS_PROD: ${{ secrets.WORKER_KEY_ALIAS_PROD }} + WORKER_KEY_PASSWORD_DEV: ${{ secrets.WORKER_KEY_PASSWORD_DEV }} + WORKER_KEY_PASSWORD_STAGING: ${{ secrets.WORKER_KEY_PASSWORD_STAGING }} + WORKER_KEY_PASSWORD_PROD: ${{ secrets.WORKER_KEY_PASSWORD_PROD }} + + # Client Mobile Secrets + CLIENT_KEYSTORE_DEV_BASE64: ${{ secrets.CLIENT_KEYSTORE_DEV_BASE64 }} + CLIENT_KEYSTORE_STAGING_BASE64: ${{ secrets.CLIENT_KEYSTORE_STAGING_BASE64 }} + CLIENT_KEYSTORE_PROD_BASE64: ${{ secrets.CLIENT_KEYSTORE_PROD_BASE64 }} + CLIENT_KEYSTORE_PASSWORD_DEV: ${{ secrets.CLIENT_KEYSTORE_PASSWORD_DEV }} + CLIENT_KEYSTORE_PASSWORD_STAGING: ${{ secrets.CLIENT_KEYSTORE_PASSWORD_STAGING }} + CLIENT_KEYSTORE_PASSWORD_PROD: ${{ secrets.CLIENT_KEYSTORE_PASSWORD_PROD }} + CLIENT_KEY_ALIAS_DEV: ${{ secrets.CLIENT_KEY_ALIAS_DEV }} + CLIENT_KEY_ALIAS_STAGING: ${{ secrets.CLIENT_KEY_ALIAS_STAGING }} + CLIENT_KEY_ALIAS_PROD: ${{ secrets.CLIENT_KEY_ALIAS_PROD }} + CLIENT_KEY_PASSWORD_DEV: ${{ secrets.CLIENT_KEY_PASSWORD_DEV }} + CLIENT_KEY_PASSWORD_STAGING: ${{ secrets.CLIENT_KEY_PASSWORD_STAGING }} + CLIENT_KEY_PASSWORD_PROD: ${{ secrets.CLIENT_KEY_PASSWORD_PROD }} + run: | + .github/scripts/setup-apk-signing.sh \ + "${{ github.event.inputs.app }}" \ + "${{ github.event.inputs.environment }}" \ + "${{ runner.temp }}" + + - name: đŸ—ī¸ Build APK + id: build_apk + run: | + APP="${{ github.event.inputs.app }}" + + if [ "$APP" = "worker-mobile-app" ]; then + echo "📱 Building Staff (Worker) APK..." + make mobile-staff-build PLATFORM=apk MODE=release + APP_NAME="staff" + else + echo "📱 Building Client APK..." + make mobile-client-build PLATFORM=apk MODE=release + APP_NAME="client" + fi + + # Find the generated APK (Flutter places it in build/app/outputs/flutter-apk/) + APK_PATH=$(find apps/mobile/apps/${APP_NAME}/build/app/outputs/flutter-apk -name "app-release.apk" 2>/dev/null | head -n 1) + + # Fallback to searching entire apps directory if not found + if [ -z "$APK_PATH" ]; then + APK_PATH=$(find apps/mobile/apps/${APP_NAME} -name "app-release.apk" | head -n 1) + fi + + if [ -z "$APK_PATH" ]; then + echo "❌ Error: APK not found!" + echo "Searched in apps/mobile/apps/${APP_NAME}/" + find apps/mobile/apps/${APP_NAME} -name "*.apk" || echo "No APK files found" + exit 1 + fi + + echo "✅ APK built successfully: $APK_PATH" + echo "app_name=${APP_NAME}" >> $GITHUB_OUTPUT + echo "apk_path=${APK_PATH}" >> $GITHUB_OUTPUT + + - name: ✅ Verify APK Signature + run: | + .github/scripts/verify-apk-signature.sh "${{ steps.build_apk.outputs.apk_path }}" + + - name: 📤 Upload APK as Artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ github.event.inputs.app }}-${{ needs.validate-and-create-release.outputs.version }}-${{ github.event.inputs.environment }} + path: apps/mobile/apps/${{ steps.build_apk.outputs.app_name }}/build/app/outputs/flutter-apk/app-release.apk + if-no-files-found: error + retention-days: 30 + + - name: đŸ“Ļ Attach APK to GitHub Release + if: ${{ github.event.inputs.create_github_release == 'true' }} + env: + GH_TOKEN: ${{ github.token }} + run: | + .github/scripts/attach-apk-to-release.sh \ + "${{ needs.validate-and-create-release.outputs.tag_name }}" \ + "${{ github.event.inputs.app }}" \ + "${{ steps.build_apk.outputs.app_name }}" \ + "${{ needs.validate-and-create-release.outputs.version }}" \ + "${{ github.event.inputs.environment }}"