Mobile e2e (@clerk/expo) #13
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" | |
| env: | |
| EXPO_INSTANCE_NAME: clerkstage-with-native-components | |
| # Override the quickstart's checked-in .npmrc, which points pnpm/npm/npx at a | |
| # local verdaccio registry (http://localhost:4873) that doesn't exist on CI. | |
| NPM_CONFIG_REGISTRY: https://registry.npmjs.org/ | |
| concurrency: | |
| group: mobile-e2e-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| android: | |
| name: Android | |
| 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 }} | |
| 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: Point quickstart at locally-built @clerk/expo | |
| working-directory: clerk-expo-quickstart/NativeComponentQuickstart | |
| run: | | |
| # The quickstart's main pins @clerk/expo to a local verdaccio | |
| # snapshot version which doesn't exist on public npm. Swap it for | |
| # the package we just built from this javascript checkout. | |
| jq '.dependencies["@clerk/expo"] = "file:../../packages/expo"' package.json > package.json.tmp | |
| mv package.json.tmp package.json | |
| - name: Install quickstart deps | |
| working-directory: clerk-expo-quickstart/NativeComponentQuickstart | |
| run: pnpm install --no-frozen-lockfile | |
| - 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" | |
| username="ci_${GITHUB_RUN_ID}_${RANDOM}" | |
| password="ClerkCI!$(openssl rand -hex 8)Aa1" | |
| http_code=$(curl -sS -o /tmp/bapi_response.json -w "%{http_code}" -X POST https://api.clerkstage.dev/v1/users \ | |
| -H "Authorization: Bearer $CLERK_SECRET_KEY" \ | |
| -H "Content-Type: application/json" \ | |
| -d "{\"email_address\":[\"$email\"],\"username\":\"$username\",\"password\":\"$password\"}") | |
| if [ "$http_code" -lt 200 ] || [ "$http_code" -ge 300 ]; then | |
| echo "::error::BAPI user creation failed (HTTP $http_code)" | |
| jq . /tmp/bapi_response.json 2>/dev/null || cat /tmp/bapi_response.json | |
| exit 1 | |
| fi | |
| response=$(cat /tmp/bapi_response.json) | |
| 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 17 | |
| uses: actions/setup-java@v4 | |
| with: | |
| distribution: temurin | |
| java-version: 17 | |
| - 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 }} | |
| EXCLUDE_TAGS: ${{ inputs.exclude_tags }} | |
| 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.clerkstage.dev/v1/users/$USER_ID" \ | |
| -H "Authorization: Bearer $CLERK_SECRET_KEY" || true | |
| ios: | |
| name: iOS | |
| 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: Point quickstart at locally-built @clerk/expo | |
| working-directory: clerk-expo-quickstart/NativeComponentQuickstart | |
| run: | | |
| # The quickstart's main pins @clerk/expo to a local verdaccio | |
| # snapshot version which doesn't exist on public npm. Swap it for | |
| # the package we just built from this javascript checkout. | |
| jq '.dependencies["@clerk/expo"] = "file:../../packages/expo"' package.json > package.json.tmp | |
| mv package.json.tmp package.json | |
| - name: Install quickstart deps | |
| working-directory: clerk-expo-quickstart/NativeComponentQuickstart | |
| run: pnpm install --no-frozen-lockfile | |
| - 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" | |
| username="ci_${GITHUB_RUN_ID}_${RANDOM}" | |
| password="ClerkCI!$(openssl rand -hex 8)Aa1" | |
| http_code=$(curl -sS -o /tmp/bapi_response.json -w "%{http_code}" -X POST https://api.clerkstage.dev/v1/users \ | |
| -H "Authorization: Bearer $CLERK_SECRET_KEY" \ | |
| -H "Content-Type: application/json" \ | |
| -d "{\"email_address\":[\"$email\"],\"username\":\"$username\",\"password\":\"$password\"}") | |
| if [ "$http_code" -lt 200 ] || [ "$http_code" -ge 300 ]; then | |
| echo "::error::BAPI user creation failed (HTTP $http_code)" | |
| jq . /tmp/bapi_response.json 2>/dev/null || cat /tmp/bapi_response.json | |
| exit 1 | |
| fi | |
| response=$(cat /tmp/bapi_response.json) | |
| 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.clerkstage.dev/v1/users/$USER_ID" \ | |
| -H "Authorization: Bearer $CLERK_SECRET_KEY" || true |