Bump Ditto dependency to 4.12.3 #335
Workflow file for this run
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: React Native CI | |
| on: | |
| pull_request: | |
| branches: | |
| - main | |
| - 'sdk-*' | |
| paths: | |
| - 'react-native/**' | |
| push: | |
| branches: | |
| - main | |
| - 'sdk-*' | |
| paths: | |
| - 'react-native/**' | |
| workflow_dispatch: | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| lint: | |
| name: Lint | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'yarn' | |
| cache-dependency-path: react-native/yarn.lock | |
| - name: Install dependencies | |
| working-directory: react-native | |
| run: yarn install --frozen-lockfile | |
| - name: Run linting | |
| working-directory: react-native | |
| run: yarn lint | |
| build-android: | |
| name: Build Android | |
| runs-on: ubuntu-latest | |
| needs: lint | |
| timeout-minutes: 30 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'yarn' | |
| cache-dependency-path: react-native/yarn.lock | |
| - name: Install dependencies | |
| working-directory: react-native | |
| run: yarn install --frozen-lockfile | |
| - name: Setup Java | |
| uses: actions/setup-java@v4 | |
| with: | |
| distribution: 'temurin' | |
| java-version: '17' | |
| - name: Setup Android SDK | |
| uses: android-actions/setup-android@v3 | |
| - name: Create .env file (production credentials for builds) | |
| run: | | |
| echo "DITTO_APP_ID=${{ secrets.DITTO_APP_ID }}" > .env | |
| echo "DITTO_PLAYGROUND_TOKEN=${{ secrets.DITTO_PLAYGROUND_TOKEN }}" >> .env | |
| echo "DITTO_AUTH_URL=${{ secrets.DITTO_AUTH_URL }}" >> .env | |
| echo "DITTO_WEBSOCKET_URL=${{ secrets.DITTO_WEBSOCKET_URL }}" >> .env | |
| - name: Cache Gradle dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.gradle/caches | |
| ~/.gradle/wrapper | |
| react-native/android/.gradle | |
| key: gradle-${{ runner.os }}-${{ hashFiles('react-native/android/gradle/wrapper/gradle-wrapper.properties', 'react-native/android/**/*.gradle*') }} | |
| restore-keys: | | |
| gradle-${{ runner.os }}- | |
| - name: Build Android APK | |
| working-directory: react-native/android | |
| # Use direct gradlew instead of yarn android to generate unsigned APK required by BrowserStack | |
| # yarn android creates signed builds that don't work with BrowserStack real device testing | |
| run: NODE_ENV=production ./gradlew assembleRelease --no-daemon --console=plain | |
| - name: Upload Android APK artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: android-apk-${{ github.run_number }} | |
| path: react-native/android/app/build/outputs/apk/release/app-release.apk | |
| retention-days: 1 | |
| build-ios: | |
| name: Build iOS | |
| runs-on: macos-latest | |
| needs: lint | |
| timeout-minutes: 30 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'yarn' | |
| cache-dependency-path: react-native/yarn.lock | |
| - name: Install dependencies | |
| working-directory: react-native | |
| run: yarn install --frozen-lockfile | |
| - name: Create .env file (production credentials for builds) | |
| run: | | |
| echo "DITTO_APP_ID=${{ secrets.DITTO_APP_ID }}" > .env | |
| echo "DITTO_PLAYGROUND_TOKEN=${{ secrets.DITTO_PLAYGROUND_TOKEN }}" >> .env | |
| echo "DITTO_AUTH_URL=${{ secrets.DITTO_AUTH_URL }}" >> .env | |
| echo "DITTO_WEBSOCKET_URL=${{ secrets.DITTO_WEBSOCKET_URL }}" >> .env | |
| - name: Cache CocoaPods dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| react-native/ios/Pods | |
| ~/Library/Caches/CocoaPods | |
| ~/.cocoapods | |
| key: cocoapods-${{ runner.os }}-${{ hashFiles('react-native/ios/Podfile.lock') }} | |
| restore-keys: | | |
| cocoapods-${{ runner.os }}- | |
| - name: Setup Xcode | |
| uses: maxim-lobanov/setup-xcode@v1 | |
| with: | |
| xcode-version: '16.4' | |
| - name: Install Ruby gems | |
| working-directory: react-native | |
| run: bundle install | |
| - name: Install CocoaPods | |
| working-directory: react-native/ios | |
| run: bundle exec pod install | |
| - name: Build iOS Archive and IPA (for BrowserStack) | |
| working-directory: react-native | |
| run: | | |
| echo "🍎 Building iOS device .ipa for BrowserStack..." | |
| # Use direct xcodebuild instead of yarn ios to generate unsigned IPA required by BrowserStack | |
| # yarn ios creates signed builds for simulators that don't work with BrowserStack real device testing | |
| # Build and archive iOS app for real device | |
| xcodebuild -workspace ios/DittoReactNativeSampleApp.xcworkspace \ | |
| -scheme DittoReactNativeSampleApp \ | |
| -configuration Release \ | |
| -destination 'generic/platform=iOS' \ | |
| -archivePath ios/build/DittoReactNativeSampleApp.xcarchive \ | |
| archive \ | |
| CODE_SIGN_IDENTITY="" \ | |
| CODE_SIGNING_REQUIRED=NO \ | |
| CODE_SIGNING_ALLOWED=NO | |
| echo "📦 Creating unsigned .ipa for BrowserStack..." | |
| # Find the .app bundle from the archive | |
| APP_BUNDLE_PATH=$(find ios/build/DittoReactNativeSampleApp.xcarchive/Products/Applications -maxdepth 1 -name "*.app" -type d | head -1) | |
| if [ -d "$APP_BUNDLE_PATH" ]; then | |
| echo "✅ iOS app bundle found: $APP_BUNDLE_PATH" | |
| # Create unsigned IPA: Payload/<App>.app zipped as .ipa | |
| mkdir -p ios/build/Payload | |
| cp -R "$APP_BUNDLE_PATH" ios/build/Payload/ | |
| (cd ios/build && zip -qry DittoReactNativeSampleApp-unsigned.ipa Payload && rm -rf Payload) | |
| if [ -f "ios/build/DittoReactNativeSampleApp-unsigned.ipa" ]; then | |
| echo "✅ Unsigned .ipa created successfully" | |
| ls -la ios/build/DittoReactNativeSampleApp-unsigned.ipa | |
| else | |
| echo "❌ Failed to create .ipa file" | |
| exit 1 | |
| fi | |
| else | |
| echo "❌ iOS app bundle not found in archive" | |
| exit 1 | |
| fi | |
| - name: Upload iOS IPA artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ios-ipa-${{ github.run_number }} | |
| path: react-native/ios/build/DittoReactNativeSampleApp-unsigned.ipa | |
| retention-days: 1 | |
| seed-ditto-cloud: | |
| name: Create test documents in Big Peer | |
| runs-on: ubuntu-latest | |
| needs: [build-android, build-ios] | |
| timeout-minutes: 10 | |
| outputs: | |
| test_task_title: ${{ steps.seed-document.outputs.document-title }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Insert test document into Ditto Cloud | |
| id: seed-document | |
| uses: ./.github/actions/seed-ditto-document | |
| with: | |
| ditto-api-key: ${{ secrets.DITTO_API_KEY }} | |
| ditto-api-url: ${{ secrets.DITTO_API_URL }} | |
| app-name: 'react-native' | |
| test-android-maestro: | |
| name: Test Android - BrowserStack Maestro | |
| runs-on: ubuntu-latest | |
| needs: [build-android, seed-ditto-cloud] | |
| timeout-minutes: 30 | |
| outputs: | |
| build_id: ${{ steps.execute-tests.outputs.build_id }} | |
| status: ${{ job.status }} | |
| # TODO: BrowserStack Android Maestro API environment variables don't work (confirmed by BS staff) | |
| # On their roadmap with estimate: Oct 2025. Will revert to setEnvVariables when ready. | |
| # Using sed workaround to replace ${MAESTRO_TASK_TO_FIND} with actual task name in test files | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Download Android APK | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: android-apk-${{ github.run_number }} | |
| path: ./artifacts/ | |
| - name: Upload APK to BrowserStack | |
| id: upload-apk | |
| run: | | |
| response=$(curl -u "${{ secrets.BROWSERSTACK_USERNAME }}:${{ secrets.BROWSERSTACK_ACCESS_KEY }}" \ | |
| -X POST "https://api-cloud.browserstack.com/app-automate/maestro/v2/app" \ | |
| -F "file=@./artifacts/app-release.apk" \ | |
| -F "custom_id=ReactNative-Android-${{ github.run_id }}") | |
| app_url=$(echo $response | jq -r '.app_url') | |
| # Validate API response | |
| if [ -z "$app_url" ] || [ "$app_url" = "null" ]; then | |
| echo "❌ Failed to get app_url from upload response" | |
| echo "Response: $response" | |
| exit 1 | |
| fi | |
| echo "✅ APK uploaded successfully: $app_url" | |
| echo "app_url=$app_url" >> $GITHUB_OUTPUT | |
| - name: Create Maestro test suite ZIP | |
| run: | | |
| cd react-native | |
| # Create proper BrowserStack folder structure (normal parent folder, not hidden) | |
| rm -rf maestro-tests | |
| mkdir maestro-tests | |
| # Replace MAESTRO_TASK_TO_FIND with actual seeded task name (BrowserStack Android API bug workaround) | |
| sed 's/\${MAESTRO_TASK_TO_FIND}/${{ needs.seed-ditto-cloud.outputs.test_task_title }}/g' \ | |
| .maestro/flows/01-app-launch-and-seeded-tasks-android.yaml > maestro-tests/01-app-launch-and-seeded-tasks-android.yaml | |
| # Also create flows/ subfolder for organization | |
| mkdir maestro-tests/flows | |
| cp .maestro/flows/01-app-launch-and-seeded-tasks-android.yaml maestro-tests/flows/ | |
| # Create ZIP with proper structure | |
| zip -r maestro-tests.zip maestro-tests -x "*.DS_Store" | |
| - name: Upload Maestro test suite to BrowserStack | |
| id: upload-tests | |
| env: | |
| BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} | |
| BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} | |
| run: | | |
| set -euo pipefail | |
| test -f react-native/maestro-tests.zip | |
| response=$(curl --fail-with-body -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_ACCESS_KEY" \ | |
| -X POST "https://api-cloud.browserstack.com/app-automate/maestro/v2/test-suite" \ | |
| -F "file=@react-native/maestro-tests.zip" \ | |
| -F "custom_id=ReactNative-Tests-${{ github.run_id }}") | |
| echo "test_suite_url=$(echo "$response" | jq -r '.test_suite_url')" >> $GITHUB_OUTPUT | |
| - name: Execute Maestro tests on BrowserStack (Parallel) | |
| id: execute-tests | |
| run: | | |
| response=$(curl -u "${{ secrets.BROWSERSTACK_USERNAME }}:${{ secrets.BROWSERSTACK_ACCESS_KEY }}" \ | |
| -X POST "https://api-cloud.browserstack.com/app-automate/maestro/v2/android/build" \ | |
| -H "Content-Type: application/json" \ | |
| -d '{ | |
| "app": "${{ steps.upload-apk.outputs.app_url }}", | |
| "testSuite": "${{ steps.upload-tests.outputs.test_suite_url }}", | |
| "project": "Ditto React Native", | |
| "buildName": "Build #${{ github.run_number }}", | |
| "buildTag": "${{ github.ref_name }}", | |
| "devices": [ | |
| "Samsung Galaxy S22-12.0", | |
| "Google Pixel 7-13.0" | |
| ], | |
| "execute": ["01-app-launch-and-seeded-tasks-android.yaml"], | |
| "deviceLogs": true, | |
| "networkLogs": true, | |
| "video": true | |
| }') | |
| echo "Execution response: $response" | |
| build_id=$(echo $response | jq -r '.build_id') | |
| echo "build_id=$build_id" >> $GITHUB_OUTPUT | |
| echo "BrowserStack Build ID: $build_id" | |
| - name: Wait for test completion and get results | |
| run: | | |
| build_id="${{ steps.execute-tests.outputs.build_id }}" | |
| # Wait for tests to complete (max 20 minutes) | |
| for i in {1..120}; do | |
| response=$(curl -s -u "${{ secrets.BROWSERSTACK_USERNAME }}:${{ secrets.BROWSERSTACK_ACCESS_KEY }}" \ | |
| "https://api-cloud.browserstack.com/app-automate/maestro/v2/builds/$build_id") | |
| status=$(echo $response | jq -r '.status') | |
| echo "Test status: $status (attempt $i/120)" | |
| if [ "$status" = "passed" ] || [ "$status" = "failed" ]; then | |
| echo "Tests completed with status: $status" | |
| echo $response | jq '.' | |
| if [ "$status" = "failed" ]; then | |
| echo "❌ Android Maestro tests failed!" | |
| exit 1 | |
| else | |
| echo "✅ Android Maestro tests passed!" | |
| fi | |
| break | |
| elif [ "$status" = "error" ]; then | |
| echo "❌ BrowserStack reported build error. Fetching session details..." | |
| echo $response | jq '.' | |
| echo $response | jq -r '.devices[]?.sessions[]?.id' 2>/dev/null | while read sid; do | |
| if [ -n "$sid" ]; then | |
| echo "Session $sid details:" | |
| curl -s -u "${{ secrets.BROWSERSTACK_USERNAME }}:${{ secrets.BROWSERSTACK_ACCESS_KEY }}" \ | |
| "https://api-cloud.browserstack.com/app-automate/maestro/v2/builds/$build_id/sessions/$sid" | jq '.' | |
| fi | |
| done | |
| exit 1 | |
| fi | |
| sleep 10 | |
| done | |
| if [ $i -eq 120 ]; then | |
| echo "❌ Tests timed out after 20 minutes" | |
| exit 1 | |
| fi | |
| test-ios-maestro: | |
| name: Test iOS - BrowserStack Maestro | |
| runs-on: ubuntu-latest | |
| needs: [build-ios, seed-ditto-cloud] | |
| timeout-minutes: 30 | |
| outputs: | |
| build_id: ${{ steps.execute-tests.outputs.build_id }} | |
| status: ${{ job.status }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Download iOS IPA | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: ios-ipa-${{ github.run_number }} | |
| path: ./artifacts/ | |
| - name: Upload IPA to BrowserStack | |
| id: upload-ipa | |
| run: | | |
| response=$(curl -u "${{ secrets.BROWSERSTACK_USERNAME }}:${{ secrets.BROWSERSTACK_ACCESS_KEY }}" \ | |
| -X POST "https://api-cloud.browserstack.com/app-automate/maestro/v2/app" \ | |
| -F "file=@./artifacts/DittoReactNativeSampleApp-unsigned.ipa" \ | |
| -F "custom_id=ReactNative-iOS-${{ github.run_id }}") | |
| app_url=$(echo $response | jq -r '.app_url') | |
| # Validate API response | |
| if [ -z "$app_url" ] || [ "$app_url" = "null" ]; then | |
| echo "❌ Failed to get app_url from upload response" | |
| echo "Response: $response" | |
| exit 1 | |
| fi | |
| echo "✅ IPA uploaded successfully: $app_url" | |
| echo "app_url=$app_url" >> $GITHUB_OUTPUT | |
| - name: Create Maestro test suite ZIP | |
| run: | | |
| cd react-native | |
| # Create proper BrowserStack folder structure (normal parent folder, not hidden) | |
| rm -rf maestro-tests | |
| mkdir maestro-tests | |
| # Copy only iOS-specific tests to root level (avoid conflicting appIds) | |
| cp .maestro/flows/01-app-launch-and-seeded-tasks-ios.yaml maestro-tests/ | |
| # Also create flows/ subfolder for organization | |
| mkdir maestro-tests/flows | |
| cp .maestro/flows/01-app-launch-and-seeded-tasks-ios.yaml maestro-tests/flows/ | |
| # Create ZIP with proper structure | |
| zip -r maestro-tests.zip maestro-tests -x "*.DS_Store" | |
| - name: Upload Maestro test suite to BrowserStack | |
| id: upload-tests | |
| run: | | |
| response=$(curl -u "${{ secrets.BROWSERSTACK_USERNAME }}:${{ secrets.BROWSERSTACK_ACCESS_KEY }}" \ | |
| -X POST "https://api-cloud.browserstack.com/app-automate/maestro/v2/test-suite" \ | |
| -F "file=@react-native/maestro-tests.zip" \ | |
| -F "custom_id=ReactNative-iOS-Tests-${{ github.run_id }}") | |
| test_suite_url=$(echo $response | jq -r '.test_suite_url') | |
| echo "test_suite_url=$test_suite_url" >> $GITHUB_OUTPUT | |
| - name: Execute Maestro tests on BrowserStack (Parallel) | |
| id: execute-tests | |
| run: | | |
| response=$(curl -u "${{ secrets.BROWSERSTACK_USERNAME }}:${{ secrets.BROWSERSTACK_ACCESS_KEY }}" \ | |
| -X POST "https://api-cloud.browserstack.com/app-automate/maestro/v2/ios/build" \ | |
| -H "Content-Type: application/json" \ | |
| -d '{ | |
| "app": "${{ steps.upload-ipa.outputs.app_url }}", | |
| "testSuite": "${{ steps.upload-tests.outputs.test_suite_url }}", | |
| "project": "Ditto React Native", | |
| "buildName": "Build #${{ github.run_number }}", | |
| "buildTag": "${{ github.ref_name }}", | |
| "devices": [ | |
| "iPhone 15-17.0", | |
| "iPhone 14-16.0" | |
| ], | |
| "execute": ["01-app-launch-and-seeded-tasks-ios.yaml"], | |
| "setEnvVariables": { | |
| "MAESTRO_TASK_TO_FIND": "${{ needs.seed-ditto-cloud.outputs.test_task_title }}" | |
| }, | |
| "deviceLogs": true, | |
| "networkLogs": true, | |
| "video": true | |
| }') | |
| echo "Execution response: $response" | |
| build_id=$(echo $response | jq -r '.build_id') | |
| echo "build_id=$build_id" >> $GITHUB_OUTPUT | |
| echo "BrowserStack Build ID: $build_id" | |
| - name: Wait for test completion and get results | |
| run: | | |
| build_id="${{ steps.execute-tests.outputs.build_id }}" | |
| # Wait for tests to complete (max 20 minutes) | |
| for i in {1..120}; do | |
| response=$(curl -s -u "${{ secrets.BROWSERSTACK_USERNAME }}:${{ secrets.BROWSERSTACK_ACCESS_KEY }}" \ | |
| "https://api-cloud.browserstack.com/app-automate/maestro/v2/builds/$build_id") | |
| status=$(echo $response | jq -r '.status') | |
| echo "Test status: $status (attempt $i/120)" | |
| if [ "$status" = "passed" ] || [ "$status" = "failed" ]; then | |
| echo "Tests completed with status: $status" | |
| echo $response | jq '.' | |
| if [ "$status" = "failed" ]; then | |
| echo "❌ iOS Maestro tests failed!" | |
| exit 1 | |
| else | |
| echo "✅ iOS Maestro tests passed!" | |
| fi | |
| break | |
| elif [ "$status" = "error" ]; then | |
| echo "❌ BrowserStack reported build error. Fetching session details..." | |
| echo $response | jq '.' | |
| echo $response | jq -r '.devices[]?.sessions[]?.id' 2>/dev/null | while read sid; do | |
| if [ -n "$sid" ]; then | |
| echo "Session $sid details:" | |
| curl -s -u "${{ secrets.BROWSERSTACK_USERNAME }}:${{ secrets.BROWSERSTACK_ACCESS_KEY }}" \ | |
| "https://api-cloud.browserstack.com/app-automate/maestro/v2/builds/$build_id/sessions/$sid" | jq '.' | |
| fi | |
| done | |
| exit 1 | |
| fi | |
| sleep 10 | |
| done | |
| if [ $i -eq 120 ]; then | |
| echo "❌ Tests timed out after 20 minutes" | |
| exit 1 | |
| fi | |
| report-results: | |
| name: Report BrowserStack Results | |
| runs-on: ubuntu-latest | |
| needs: [test-android-maestro, test-ios-maestro] | |
| if: always() | |
| steps: | |
| - name: Generate BrowserStack Summary | |
| run: | | |
| echo "## 📱 React Native - BrowserStack Maestro Test Results" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| # Android results | |
| echo "### Android Tests" >> $GITHUB_STEP_SUMMARY | |
| ANDROID_BUILD_ID="${{ needs.test-android-maestro.outputs.build_id }}" | |
| ANDROID_STATUS="${{ needs.test-android-maestro.outputs.status }}" | |
| if [ -n "$ANDROID_BUILD_ID" ] && [ "$ANDROID_BUILD_ID" != "null" ]; then | |
| if [ "$ANDROID_STATUS" = "success" ]; then | |
| echo "✅ **Status:** Passed" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "❌ **Status:** Failed" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "🔗 **[View Dashboard](https://app-automate.browserstack.com/dashboard/v2/builds/$ANDROID_BUILD_ID)**" >> $GITHUB_STEP_SUMMARY | |
| echo "Build ID: \`$ANDROID_BUILD_ID\`" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "❌ **Status:** Failed (No build created)" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| # iOS results | |
| echo "### iOS Tests" >> $GITHUB_STEP_SUMMARY | |
| IOS_BUILD_ID="${{ needs.test-ios-maestro.outputs.build_id }}" | |
| IOS_STATUS="${{ needs.test-ios-maestro.outputs.status }}" | |
| if [ -n "$IOS_BUILD_ID" ] && [ "$IOS_BUILD_ID" != "null" ]; then | |
| if [ "$IOS_STATUS" = "success" ]; then | |
| echo "✅ **Status:** Passed" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "❌ **Status:** Failed" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "🔗 **[View Dashboard](https://app-automate.browserstack.com/dashboard/v2/builds/$IOS_BUILD_ID)**" >> $GITHUB_STEP_SUMMARY | |
| echo "Build ID: \`$IOS_BUILD_ID\`" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "❌ **Status:** Failed (No build created)" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| # Summary | |
| echo "---" >> $GITHUB_STEP_SUMMARY | |
| if [ "$ANDROID_STATUS" = "success" ] && [ "$IOS_STATUS" = "success" ]; then | |
| echo "✅ **Overall Result:** All tests passed" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "⚠️ **Overall Result:** Some tests failed" >> $GITHUB_STEP_SUMMARY | |
| fi |