-
Notifications
You must be signed in to change notification settings - Fork 452
ci(expo): add manual mobile e2e workflow_dispatch #8489
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,269 @@ | ||
| # 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_INSTANCE_KEYS — JSON map of named test instances | ||
| # ({ "<name>": { "pk": "pk_test_...", "sk": "sk_test_..." } }). | ||
| # Same secret used by /integration (Playwright). 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. | ||
| # | ||
| # TODO(SDK team): confirm the instance-name slot to add inside | ||
| # INTEGRATION_INSTANCE_KEYS for this workflow (placeholder: "expo-native"). | ||
| 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: | ||
| # TODO(SDK team): replace with the canonical mobile-e2e instance name once confirmed. | ||
| EXPO_INSTANCE_NAME: expo-native | ||
|
|
||
| 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: Install quickstart deps | ||
| working-directory: clerk-expo-quickstart/NativeComponentQuickstart | ||
| run: pnpm install | ||
|
|
||
| - name: Resolve Clerk instance keys | ||
| id: keys | ||
| env: | ||
| INTEGRATION_INSTANCE_KEYS: ${{ secrets.INTEGRATION_INSTANCE_KEYS }} | ||
| run: | | ||
| if [ -z "$INTEGRATION_INSTANCE_KEYS" ]; then | ||
| echo "::error::INTEGRATION_INSTANCE_KEYS secret is not set" | ||
| exit 1 | ||
| fi | ||
| pk=$(echo "$INTEGRATION_INSTANCE_KEYS" | jq -er ".[\"$EXPO_INSTANCE_NAME\"].pk") || { | ||
| echo "::error::No entry '$EXPO_INSTANCE_NAME' found in INTEGRATION_INSTANCE_KEYS" | ||
| exit 1 | ||
| } | ||
| sk=$(echo "$INTEGRATION_INSTANCE_KEYS" | jq -er ".[\"$EXPO_INSTANCE_NAME\"].sk") | ||
| echo "::add-mask::$sk" | ||
| echo "pk=$pk" >> "$GITHUB_OUTPUT" | ||
| echo "sk=$sk" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - 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" | ||
|
Comment on lines
+100
to
+111
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Register The mask directive on line 108 (and 225 in the iOS job) runs after 🔒 Proposed fix (apply to both jobs) email="ci-${GITHUB_RUN_ID}-${RANDOM}+clerk_test@clerkcookie.com"
password="ClerkCI!$(openssl rand -hex 8)Aa1"
+ echo "::add-mask::$password"
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"Optionally, also build the JSON body via Also applies to: 217-228 🤖 Prompt for AI Agents |
||
|
|
||
| - 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.clerk.com/v1/users/$USER_ID" \ | ||
| -H "Authorization: Bearer $CLERK_SECRET_KEY" || true | ||
|
|
||
| ios: | ||
Check warningCode scanning / CodeQL Workflow does not contain permissions Medium
Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}
|
||
|
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment on lines
+40
to
+159
|
||
| 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: Install quickstart deps | ||
| working-directory: clerk-expo-quickstart/NativeComponentQuickstart | ||
| run: pnpm install | ||
|
|
||
| - name: Resolve Clerk instance keys | ||
| id: keys | ||
| env: | ||
| INTEGRATION_INSTANCE_KEYS: ${{ secrets.INTEGRATION_INSTANCE_KEYS }} | ||
| run: | | ||
| if [ -z "$INTEGRATION_INSTANCE_KEYS" ]; then | ||
| echo "::error::INTEGRATION_INSTANCE_KEYS secret is not set" | ||
| exit 1 | ||
| fi | ||
| pk=$(echo "$INTEGRATION_INSTANCE_KEYS" | jq -er ".[\"$EXPO_INSTANCE_NAME\"].pk") || { | ||
| echo "::error::No entry '$EXPO_INSTANCE_NAME' found in INTEGRATION_INSTANCE_KEYS" | ||
| exit 1 | ||
| } | ||
| sk=$(echo "$INTEGRATION_INSTANCE_KEYS" | jq -er ".[\"$EXPO_INSTANCE_NAME\"].sk") | ||
| echo "::add-mask::$sk" | ||
| echo "pk=$pk" >> "$GITHUB_OUTPUT" | ||
| echo "sk=$sk" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - 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" | ||
|
|
||
|
Comment on lines
+246
to
+254
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
🥳 Fixed in commit ed6dc98 🥳 |
||
| - 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 | ||
Check warningCode scanning / CodeQL Workflow does not contain permissions Medium
Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}
|
||
|
Comment on lines
+160
to
+269
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pass
pkviaenv:instead of${{ … }}interpolation in therun:script.pkoriginates from a parsed secret, so the practical injection risk is low, but the same anti-pattern was already addressed in this PR forexclude_tags(now using theEXCLUDE_TAGSenv var). For consistency with that fix and the Semgrep guidance previously applied here, routepkthroughenv:as well — this also avoids quoting issues if the publishable key ever contains a"or backslash. Apply the same change in both Android (line 91–94) and iOS (line 208–211) jobs.🔒 Proposed fix
- name: Write quickstart .env working-directory: clerk-expo-quickstart/NativeComponentQuickstart + env: + PUBLISHABLE_KEY: ${{ steps.keys.outputs.pk }} run: | - echo "EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY=${{ steps.keys.outputs.pk }}" > .env + printf 'EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY=%s\n' "$PUBLISHABLE_KEY" > .envAlso applies to: 208-211
🤖 Prompt for AI Agents