Mobile e2e (@clerk/expo) #42
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
| # Manual mobile e2e for @clerk/expo native components. | |
| # Clones clerk-expo-quickstart, builds the NativeComponentQuickstart app, | |
| # and runs Maestro flows on iOS simulator and Android emulator. | |
| # | |
| # Secrets: | |
| # INTEGRATION_STAGING_INSTANCE_KEYS — JSON map of named staging test instances | |
| # ({ "<name>": { "pk": "pk_test_...", "sk": "sk_test_..." } }). | |
| # Same secret used by /integration (Playwright) staging jobs. We read the | |
| # entry named EXPO_INSTANCE_NAME (set in env: below). | |
| # | |
| # Test users are provisioned per-run via Clerk Backend API and deleted at | |
| # teardown — same pattern as /integration's createBapiUser. | |
| name: "Mobile e2e (@clerk/expo)" | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| quickstart_ref: | |
| description: "clerk-expo-quickstart git ref (branch, tag, or SHA)" | |
| required: false | |
| default: "main" | |
| exclude_tags: | |
| description: "Maestro tags to exclude (comma-separated)" | |
| required: false | |
| default: "manual,skip" | |
| clerk_android_ref: | |
| description: "clerk-android git ref to publish as a mavenLocal snapshot and test @clerk/expo against. Leave empty to test the versions already pinned in packages/expo/android/build.gradle." | |
| required: false | |
| default: "" | |
| clerk_android_snapshot_suffix: | |
| description: "Suffix appended to the clerk-android version when clerk_android_ref is set (e.g. '-compat-12345678'). Required when clerk_android_ref is set." | |
| required: false | |
| default: "" | |
| # Auto-run on Renovate PRs that bump the clerkAndroid* pins. The dep bump is | |
| # already applied at the PR's merge ref, so we just need to build + Maestro | |
| # against it — no clerk-android checkout/snapshot publish is needed in this path. | |
| pull_request: | |
| paths: | |
| - "packages/expo/android/build.gradle" | |
| env: | |
| EXPO_INSTANCE_NAME: clerkstage-with-native-components | |
| concurrency: | |
| group: mobile-e2e-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| android: | |
| name: Android | |
| # Restrict pull_request runs to Renovate to avoid burning emulator time on | |
| # human PRs that touch the same path. Manual dispatches always run. | |
| if: github.event_name != 'pull_request' || github.event.pull_request.user.login == 'renovate[bot]' | |
| runs-on: 'blacksmith-8vcpu-ubuntu-2204' | |
| timeout-minutes: 45 | |
| defaults: | |
| run: | |
| working-directory: . | |
| steps: | |
| - name: Checkout @clerk/javascript | |
| uses: actions/checkout@v4 | |
| - name: Checkout clerk-expo-quickstart | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: clerk/clerk-expo-quickstart | |
| ref: ${{ inputs.quickstart_ref || 'main' }} | |
| path: clerk-expo-quickstart | |
| - name: Checkout clerk-android | |
| if: inputs.clerk_android_ref != '' | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: clerk/clerk-android | |
| ref: ${{ inputs.clerk_android_ref }} | |
| path: clerk-android | |
| - uses: pnpm/action-setup@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| cache: pnpm | |
| - name: Install monorepo deps | |
| run: pnpm install --frozen-lockfile | |
| - name: Build @clerk/expo | |
| run: pnpm turbo build --filter=@clerk/expo... | |
| - name: Install quickstart deps | |
| working-directory: clerk-expo-quickstart/NativeComponentQuickstart | |
| run: pnpm install | |
| - name: Resolve Clerk instance keys | |
| id: keys | |
| env: | |
| INTEGRATION_STAGING_INSTANCE_KEYS: ${{ secrets.INTEGRATION_STAGING_INSTANCE_KEYS }} | |
| run: node scripts/resolve-instance-keys.mjs INTEGRATION_STAGING_INSTANCE_KEYS "$EXPO_INSTANCE_NAME" | |
| - name: Write quickstart .env | |
| working-directory: clerk-expo-quickstart/NativeComponentQuickstart | |
| run: | | |
| echo "EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY=${{ steps.keys.outputs.pk }}" > .env | |
| - name: Provision test user via BAPI | |
| id: user | |
| env: | |
| CLERK_SECRET_KEY: ${{ steps.keys.outputs.sk }} | |
| run: | | |
| email="ci-${GITHUB_RUN_ID}-${RANDOM}+clerk_test@clerkcookie.com" | |
| password="ClerkCI!$(openssl rand -hex 8)Aa1" | |
| response=$(curl -fsS -X POST https://api.clerk.com/v1/users \ | |
| -H "Authorization: Bearer $CLERK_SECRET_KEY" \ | |
| -H "Content-Type: application/json" \ | |
| -d "{\"email_address\":[\"$email\"],\"password\":\"$password\"}") | |
| user_id=$(echo "$response" | jq -er '.id') | |
| echo "::add-mask::$password" | |
| echo "email=$email" >> "$GITHUB_OUTPUT" | |
| echo "password=$password" >> "$GITHUB_OUTPUT" | |
| echo "user_id=$user_id" >> "$GITHUB_OUTPUT" | |
| - name: Set up JDK 21 | |
| uses: actions/setup-java@v4 | |
| with: | |
| distribution: temurin | |
| # 21 (not 17) because clerk-android requires it to build. Expo's | |
| # quickstart Android build works with 21 as well. | |
| java-version: 21 | |
| - name: Validate clerk-android snapshot inputs | |
| if: inputs.clerk_android_ref != '' && inputs.clerk_android_snapshot_suffix == '' | |
| run: | | |
| echo "::error::clerk_android_ref is set but clerk_android_snapshot_suffix is empty. Provide a suffix (e.g. '-compat-${{ github.run_id }}')." | |
| exit 1 | |
| - name: Compute clerk-android snapshot versions | |
| id: android_versions | |
| if: inputs.clerk_android_ref != '' | |
| working-directory: clerk-android | |
| run: | | |
| suffix="${{ inputs.clerk_android_snapshot_suffix }}" | |
| api="$(awk -F= '/^CLERK_API_VERSION=/{print $2}' gradle.properties | tr -d '[:space:]')${suffix}" | |
| ui="$(awk -F= '/^CLERK_UI_VERSION=/{print $2}' gradle.properties | tr -d '[:space:]')${suffix}" | |
| telemetry="$(awk -F= '/^CLERK_TELEMETRY_VERSION=/{print $2}' gradle.properties | tr -d '[:space:]')${suffix}" | |
| echo "api=$api" >> "$GITHUB_OUTPUT" | |
| echo "ui=$ui" >> "$GITHUB_OUTPUT" | |
| echo "telemetry=$telemetry" >> "$GITHUB_OUTPUT" | |
| - name: Publish clerk-android snapshot to mavenLocal | |
| if: inputs.clerk_android_ref != '' | |
| working-directory: clerk-android | |
| run: | | |
| sed -i "s/^CLERK_API_VERSION=.*/CLERK_API_VERSION=${{ steps.android_versions.outputs.api }}/" gradle.properties | |
| sed -i "s/^CLERK_UI_VERSION=.*/CLERK_UI_VERSION=${{ steps.android_versions.outputs.ui }}/" gradle.properties | |
| sed -i "s/^CLERK_TELEMETRY_VERSION=.*/CLERK_TELEMETRY_VERSION=${{ steps.android_versions.outputs.telemetry }}/" gradle.properties | |
| chmod +x gradlew | |
| ./gradlew :source:api:publishToMavenLocal :source:ui:publishToMavenLocal :source:telemetry:publishToMavenLocal -x signMavenPublication | |
| - name: Patch @clerk/expo build.gradle to use snapshot | |
| if: inputs.clerk_android_ref != '' | |
| run: | | |
| sed -i "s/clerkAndroidApiVersion = \".*\"/clerkAndroidApiVersion = \"${{ steps.android_versions.outputs.api }}\"/" packages/expo/android/build.gradle | |
| sed -i "s/clerkAndroidUiVersion = \".*\"/clerkAndroidUiVersion = \"${{ steps.android_versions.outputs.ui }}\"/" packages/expo/android/build.gradle | |
| # Expo autolinks via the root project's repositories; adding mavenLocal() | |
| # to the expo module's build.gradle is sufficient for gradle resolution. | |
| if ! grep -q "mavenLocal()" packages/expo/android/build.gradle; then | |
| sed -i '/^repositories\s*{/a \ mavenLocal()' packages/expo/android/build.gradle || true | |
| fi | |
| echo "=== Patched @clerk/expo build.gradle ===" | |
| grep -A 2 -B 2 "clerkAndroid\|mavenLocal" packages/expo/android/build.gradle | head -20 | |
| - name: Install Maestro | |
| run: | | |
| curl -Ls "https://get.maestro.mobile.dev" | bash | |
| echo "$HOME/.maestro/bin" >> "$GITHUB_PATH" | |
| - name: Run Android e2e | |
| uses: reactivecircus/android-emulator-runner@v2 | |
| env: | |
| CLERK_TEST_EMAIL: ${{ steps.user.outputs.email }} | |
| CLERK_TEST_PASSWORD: ${{ steps.user.outputs.password }} | |
| # inputs.exclude_tags is empty on pull_request triggers; fall back to the same default | |
| EXCLUDE_TAGS: ${{ inputs.exclude_tags || 'manual,skip' }} | |
| with: | |
| api-level: 34 | |
| target: google_apis | |
| arch: x86_64 | |
| script: | | |
| cd clerk-expo-quickstart/NativeComponentQuickstart | |
| npx expo prebuild --clean | |
| npx expo run:android --variant release --no-bundler | |
| cd ../../integration-mobile | |
| # Maestro doesn't auto-recurse into subdirectories; pass each flow explicitly. | |
| find flows -type f -name "*.yaml" ! -path "*/common/*" -print0 | \ | |
| xargs -0 maestro test --exclude-tags "$EXCLUDE_TAGS" | |
| - name: Upload Maestro artifacts on failure | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: maestro-android | |
| path: ~/.maestro/tests | |
| - name: Cleanup test user | |
| if: always() && steps.user.outputs.user_id != '' | |
| env: | |
| CLERK_SECRET_KEY: ${{ steps.keys.outputs.sk }} | |
| USER_ID: ${{ steps.user.outputs.user_id }} | |
| run: | | |
| curl -fsS -X DELETE "https://api.clerk.com/v1/users/$USER_ID" \ | |
| -H "Authorization: Bearer $CLERK_SECRET_KEY" || true | |
| ios: | |
| name: iOS | |
| # iOS isn't affected by the Android pin bump path filter; only run on manual dispatch. | |
| if: github.event_name == 'workflow_dispatch' | |
| runs-on: macos-15 | |
| timeout-minutes: 60 | |
| steps: | |
| - name: Checkout @clerk/javascript | |
| uses: actions/checkout@v4 | |
| - name: Checkout clerk-expo-quickstart | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: clerk/clerk-expo-quickstart | |
| ref: ${{ inputs.quickstart_ref }} | |
| path: clerk-expo-quickstart | |
| - uses: pnpm/action-setup@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| cache: pnpm | |
| - name: Install monorepo deps | |
| run: pnpm install --frozen-lockfile | |
| - name: Build @clerk/expo | |
| run: pnpm turbo build --filter=@clerk/expo... | |
| - name: Install quickstart deps | |
| working-directory: clerk-expo-quickstart/NativeComponentQuickstart | |
| run: pnpm install | |
| - name: Resolve Clerk instance keys | |
| id: keys | |
| env: | |
| INTEGRATION_STAGING_INSTANCE_KEYS: ${{ secrets.INTEGRATION_STAGING_INSTANCE_KEYS }} | |
| run: node scripts/resolve-instance-keys.mjs INTEGRATION_STAGING_INSTANCE_KEYS "$EXPO_INSTANCE_NAME" | |
| - name: Write quickstart .env | |
| working-directory: clerk-expo-quickstart/NativeComponentQuickstart | |
| run: | | |
| echo "EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY=${{ steps.keys.outputs.pk }}" > .env | |
| - name: Provision test user via BAPI | |
| id: user | |
| env: | |
| CLERK_SECRET_KEY: ${{ steps.keys.outputs.sk }} | |
| run: | | |
| email="ci-${GITHUB_RUN_ID}-${RANDOM}+clerk_test@clerkcookie.com" | |
| password="ClerkCI!$(openssl rand -hex 8)Aa1" | |
| response=$(curl -fsS -X POST https://api.clerk.com/v1/users \ | |
| -H "Authorization: Bearer $CLERK_SECRET_KEY" \ | |
| -H "Content-Type: application/json" \ | |
| -d "{\"email_address\":[\"$email\"],\"password\":\"$password\"}") | |
| user_id=$(echo "$response" | jq -er '.id') | |
| echo "::add-mask::$password" | |
| echo "email=$email" >> "$GITHUB_OUTPUT" | |
| echo "password=$password" >> "$GITHUB_OUTPUT" | |
| echo "user_id=$user_id" >> "$GITHUB_OUTPUT" | |
| - name: Cache SPM | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/Library/Developer/Xcode/DerivedData | |
| key: spm-${{ hashFiles('packages/expo/package.json') }} | |
| - name: Install Maestro | |
| run: | | |
| curl -Ls "https://get.maestro.mobile.dev" | bash | |
| echo "$HOME/.maestro/bin" >> "$GITHUB_PATH" | |
| - name: Build and run iOS e2e | |
| env: | |
| CLERK_TEST_EMAIL: ${{ steps.user.outputs.email }} | |
| CLERK_TEST_PASSWORD: ${{ steps.user.outputs.password }} | |
| EXCLUDE_TAGS: ${{ inputs.exclude_tags }} | |
| run: | | |
| cd clerk-expo-quickstart/NativeComponentQuickstart | |
| npx expo prebuild --clean | |
| npx expo run:ios --configuration Release --no-bundler | |
| cd ../../integration-mobile | |
| # Maestro doesn't auto-recurse into subdirectories; pass each flow explicitly. | |
| find flows -type f -name "*.yaml" ! -path "*/common/*" -print0 | \ | |
| xargs -0 maestro test --exclude-tags "$EXCLUDE_TAGS,androidOnly" | |
| - name: Upload Maestro artifacts on failure | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: maestro-ios | |
| path: ~/.maestro/tests | |
| - name: Cleanup test user | |
| if: always() && steps.user.outputs.user_id != '' | |
| env: | |
| CLERK_SECRET_KEY: ${{ steps.keys.outputs.sk }} | |
| USER_ID: ${{ steps.user.outputs.user_id }} | |
| run: | | |
| curl -fsS -X DELETE "https://api.clerk.com/v1/users/$USER_ID" \ | |
| -H "Authorization: Bearer $CLERK_SECRET_KEY" || true |