Skip to content

ci(expo): add manual mobile e2e workflow_dispatch#8489

Merged
wobsoriano merged 3 commits intomainfrom
chris/mobile-e2e-workflow
May 6, 2026
Merged

ci(expo): add manual mobile e2e workflow_dispatch#8489
wobsoriano merged 3 commits intomainfrom
chris/mobile-e2e-workflow

Conversation

@chriscanin
Copy link
Copy Markdown
Member

Summary

Splits the mobile-e2e.yml workflow file out of #8334 so it lands on main first.

workflow_dispatch only registers in the GitHub Actions UI once the workflow file is on the default branch. Landing this small PR first lets us dispatch the workflow against chris/native-component-tests and validate the full mobile e2e end-to-end before merging the larger PR.

The workflow itself is manual-only:

  • Does not run on push, PR, or schedule.
  • Triggered via Actions UI → "Run workflow" → "Mobile e2e (@clerk/expo)" or via gh workflow run mobile-e2e.yml.
  • Reads the test instance keys from the existing INTEGRATION_STAGING_INSTANCE_KEYS repo secret (entry clerkstage-with-native-components, added separately by the SDK team).
  • Provisions a per-run BAPI test user against the staging instance and cleans it up on teardown.

The test infrastructure that this workflow runs (Maestro flows under integration-mobile/, the new packages/expo unit tests, native Swift/Kotlin tests) lives in #8334 and will be merged separately.

Test plan

  • Merge this PR to main.
  • SDK team adds the clerkstage-with-native-components entry to the INTEGRATION_STAGING_INSTANCE_KEYS repo secret (already in 1Password's Shared/JS SDKs integration tests note).
  • Dispatch the workflow against chris/native-component-tests from the Actions UI; confirm both Android and iOS jobs come back green.
  • Once green, mark test(expo): comprehensive test coverage for native components #8334 ready for review.

…omponents

Lands the workflow file ahead of #8334 so that 'workflow_dispatch'
shows up in the Actions UI and can be dispatched against the feature
branch for end-to-end validation before the larger PR is merged.

The workflow itself does nothing on push or PR — it is manual-only.
Test infrastructure (Maestro flows, integration-mobile/, packages/expo
unit tests) lives in #8334 and will be merged separately.
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 6, 2026

⚠️ No Changeset found

Latest commit: 2f3694a

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel
Copy link
Copy Markdown

vercel Bot commented May 6, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
clerk-js-sandbox Ready Ready Preview, Comment May 6, 2026 10:25pm

Request Review

Comment on lines +244 to +252
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 "${{ inputs.exclude_tags }},androidOnly"

Copy link
Copy Markdown

@semgrep-code-clerk semgrep-code-clerk Bot May 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using variable interpolation ${{...}} with github context data in a run: step could allow an attacker to inject their own code into the runner. This would allow them to steal secrets and code. github context data can have arbitrary user input and should be treated as untrusted. Instead, use an intermediate environment variable with env: to store the data and use the environment variable in the run: script. Be sure to use double-quotes the environment variable, like this: "$ENVVAR".

🥳 Fixed in commit ed6dc98 🥳

@chriscanin chriscanin changed the title ci(mobile-e2e): add @clerk/expo manual workflow ci(expo): add manual mobile e2e workflow_dispatch May 6, 2026
Comment thread .github/workflows/mobile-e2e.yml Fixed
Comment on lines +160 to +269
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"

- 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
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 6, 2026

📝 Walkthrough

Walkthrough

Adds a new GitHub Actions workflow that runs mobile end-to-end tests for Clerk Expo with two jobs: Android (Ubuntu + Android emulator) and iOS (macOS + iOS simulator). Each job checks out the repo and a clerk-expo quickstart, sets up Node/pnpm, builds @clerk/expo, installs quickstart dependencies, resolves instance pk/sk from INTEGRATION_INSTANCE_KEYS and writes a Quickstart .env, provisions a per-run Clerk test user via the Clerk Backend API (masking sensitive values), runs Maestro E2E flows from integration-mobile (iOS excludes androidOnly flows), uploads Maestro artifacts on failure, and deletes the test user in an always() cleanup step.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding a manual mobile e2e workflow with workflow_dispatch trigger to the GitHub Actions configuration.
Description check ✅ Passed The description is well-related to the changeset, explaining the rationale for extracting the workflow, its manual-only behavior, and the test plan for validation.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 6, 2026

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@8489

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@8489

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@8489

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@8489

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@8489

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@8489

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@8489

@clerk/express

npm i https://pkg.pr.new/@clerk/express@8489

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@8489

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@8489

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@8489

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@8489

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@8489

@clerk/react

npm i https://pkg.pr.new/@clerk/react@8489

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@8489

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@8489

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@8489

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@8489

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@8489

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@8489

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@8489

commit: 2f3694a

@chriscanin chriscanin requested a review from wobsoriano May 6, 2026 21:24
Copy link
Copy Markdown
Member

@wobsoriano wobsoriano left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

left a question, but looks good here

Comment thread .github/workflows/mobile-e2e.yml Outdated
jobs:
android:
name: Android
runs-on: ubuntu-latest
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other heavier CI workflows in this repo use Blacksmith runners, so it may be worth switching this Android job as well if we want consistency or better performance. If theres a reason not to use Blacksmith for this workflow, itd be good to document it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not pushing to use the blacksmith runner, just curious 👀

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would probably be a good use case, will use the blacksmith runner!

Comment on lines +40 to +159
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"

- 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:
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
.github/workflows/mobile-e2e.yml (1)

16-19: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add an explicit top-level permissions: block.

The workflow has no permissions: declaration, so the GITHUB_TOKEN falls back to the repository default (often contents: write and broader scopes). Since none of the steps need write access, lock the token down. CodeQL has flagged this on both jobs.

🔒 Proposed fix
 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"
 
+permissions:
+  contents: read
+
 env:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/mobile-e2e.yml around lines 16 - 19, Add an explicit
top-level permissions block for the "Mobile e2e (`@clerk/expo`)" workflow to
restrict the GITHUB_TOKEN; add a permissions: block (e.g., permissions:
contents: read) directly under the existing on: declaration so the workflow uses
a read-only token for repository contents (adjust or add other minimal scopes
only if a step truly requires them).
🧹 Nitpick comments (1)
.github/workflows/mobile-e2e.yml (1)

230-234: ⚡ Quick win

SPM cache scope and key don’t match what’s actually being cached.

A couple of issues with this cache step:

  • Path is too broad. ~/Library/Developer/Xcode/DerivedData contains all Xcode build outputs (module caches, indexes, archives), not just SPM packages, and many of those artifacts are tied to the absolute path of the generated Xcode project. Since npx expo prebuild --clean regenerates ios/ each run, large parts of this cache will be stale or unusable. For SPM specifically, prefer caching ~/Library/Caches/org.swift.swiftpm and the SourcePackages/checkouts subtree under DerivedData.
  • Key doesn’t reflect SPM dependencies. SPM deps are driven by the quickstart’s native config (Expo modules pulled in by clerk-expo-quickstart/NativeComponentQuickstart), not by packages/expo/package.json. The current key will hit the cache even when SPM deps materially change, and miss when only @clerk/expo package metadata changes.
  • No restore-keys, so any miss = full re-download.
♻️ Suggested adjustment
       - name: Cache SPM
         uses: actions/cache@v4
         with:
-          path: ~/Library/Developer/Xcode/DerivedData
-          key: spm-${{ hashFiles('packages/expo/package.json') }}
+          path: |
+            ~/Library/Caches/org.swift.swiftpm
+            ~/Library/Developer/Xcode/DerivedData/**/SourcePackages/checkouts
+          key: spm-${{ runner.os }}-${{ hashFiles('clerk-expo-quickstart/NativeComponentQuickstart/package.json', 'clerk-expo-quickstart/NativeComponentQuickstart/app.json', 'packages/expo/package.json') }}
+          restore-keys: |
+            spm-${{ runner.os }}-

Note: the quickstart isn’t checked out at the time hashFiles is evaluated for the cache restore phase — actions/cache evaluates hashFiles at step run time, which is fine here because the “Checkout clerk-expo-quickstart” step runs earlier in the job. Worth a quick sanity check on the first dispatch.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/mobile-e2e.yml around lines 230 - 234, The "Cache SPM"
step uses the wrong path and key: replace the broad path
`~/Library/Developer/Xcode/DerivedData` with the SPM-specific caches
(`~/Library/Caches/org.swift.swiftpm` and the repo's iOS SPM checkout folder
`ios/SourcePackages/checkouts` or `ios/SourcePackages` subtree) and update the
cache key (currently `spm-${{ hashFiles('packages/expo/package.json') }}`) to be
driven by the native SPM manifests that control dependencies (e.g., hash the
quickstart/native project manifest(s) such as `ios/Package.resolved` or whatever
native quickstart files determine SPM deps) so the key reflects actual SPM
changes, and add `restore-keys` fallback patterns to allow partial/cache hits;
ensure the checkout step that brings the quickstart runs before this cache step
so the hashFiles evaluation can see those native files.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/mobile-e2e.yml:
- Around line 100-111: The password masking is registered too late—move the echo
"::add-mask::$password" call to immediately after the password is generated
(right after the line that sets the password variable) in both the Android and
iOS job blocks (refer to the password variable and the existing mask echo and
curl POST request) so the mask is active before curl or any other command can
emit the secret; optionally, build the JSON body with jq (e.g., using jq -n
--arg ...) instead of shell string interpolation to avoid quoting issues when
sending the POST.
- Around line 91-94: Update the Android and iOS job steps that currently
interpolate steps.keys.outputs.pk inside the run block; instead expose the value
via env: (e.g., set EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY: ${{ steps.keys.outputs.pk
}}) and change the run script to reference the env var (use
$EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY) when writing the .env file; locate the steps
where steps.keys.outputs.pk is used and replace the inline interpolation with
the env binding in both job blocks.

---

Duplicate comments:
In @.github/workflows/mobile-e2e.yml:
- Around line 16-19: Add an explicit top-level permissions block for the "Mobile
e2e (`@clerk/expo`)" workflow to restrict the GITHUB_TOKEN; add a permissions:
block (e.g., permissions: contents: read) directly under the existing on:
declaration so the workflow uses a read-only token for repository contents
(adjust or add other minimal scopes only if a step truly requires them).

---

Nitpick comments:
In @.github/workflows/mobile-e2e.yml:
- Around line 230-234: The "Cache SPM" step uses the wrong path and key: replace
the broad path `~/Library/Developer/Xcode/DerivedData` with the SPM-specific
caches (`~/Library/Caches/org.swift.swiftpm` and the repo's iOS SPM checkout
folder `ios/SourcePackages/checkouts` or `ios/SourcePackages` subtree) and
update the cache key (currently `spm-${{ hashFiles('packages/expo/package.json')
}}`) to be driven by the native SPM manifests that control dependencies (e.g.,
hash the quickstart/native project manifest(s) such as `ios/Package.resolved` or
whatever native quickstart files determine SPM deps) so the key reflects actual
SPM changes, and add `restore-keys` fallback patterns to allow partial/cache
hits; ensure the checkout step that brings the quickstart runs before this cache
step so the hashFiles evaluation can see those native files.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 00d1c2bd-58e9-475c-b52b-4e36c76c585c

📥 Commits

Reviewing files that changed from the base of the PR and between d7317c1 and 2f3694a.

📒 Files selected for processing (1)
  • .github/workflows/mobile-e2e.yml

Comment on lines +91 to +94
- name: Write quickstart .env
working-directory: clerk-expo-quickstart/NativeComponentQuickstart
run: |
echo "EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY=${{ steps.keys.outputs.pk }}" > .env
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Pass pk via env: instead of ${{ … }} interpolation in the run: script.

pk originates from a parsed secret, so the practical injection risk is low, but the same anti-pattern was already addressed in this PR for exclude_tags (now using the EXCLUDE_TAGS env var). For consistency with that fix and the Semgrep guidance previously applied here, route pk through env: 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" > .env

Also applies to: 208-211

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/mobile-e2e.yml around lines 91 - 94, Update the Android
and iOS job steps that currently interpolate steps.keys.outputs.pk inside the
run block; instead expose the value via env: (e.g., set
EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY: ${{ steps.keys.outputs.pk }}) and change the
run script to reference the env var (use $EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY)
when writing the .env file; locate the steps where steps.keys.outputs.pk is used
and replace the inline interpolation with the env binding in both job blocks.

Comment on lines +100 to +111
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"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Register ::add-mask:: immediately after generating the password.

The mask directive on line 108 (and 225 in the iOS job) runs after curl has already used $password. With -fsS, curl is normally silent, but on a non-2xx response it can dump the request body to stderr — which would surface the unmasked password in the workflow log before the mask is registered. Move the mask call to right after the password is generated, so it's always in effect before the value can appear anywhere.

🔒 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 jq -n --arg email "$email" --arg password "$password" '{email_address:[$email],password:$password}' to avoid shell-quoting fragility if either value ever contains " or \.

Also applies to: 217-228

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/mobile-e2e.yml around lines 100 - 111, The password
masking is registered too late—move the echo "::add-mask::$password" call to
immediately after the password is generated (right after the line that sets the
password variable) in both the Android and iOS job blocks (refer to the password
variable and the existing mask echo and curl POST request) so the mask is active
before curl or any other command can emit the secret; optionally, build the JSON
body with jq (e.g., using jq -n --arg ...) instead of shell string interpolation
to avoid quoting issues when sending the POST.

@wobsoriano
Copy link
Copy Markdown
Member

Merging this to unblock the next PR.

@wobsoriano wobsoriano merged commit 8bfea20 into main May 6, 2026
73 of 74 checks passed
@wobsoriano wobsoriano deleted the chris/mobile-e2e-workflow branch May 6, 2026 22:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants