ci(expo): add manual mobile e2e workflow_dispatch#8489
Conversation
…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.
|
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
| 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" | ||
|
|
There was a problem hiding this comment.
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 🥳
| 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 |
📝 WalkthroughWalkthroughAdds 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 Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ 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. Comment |
@clerk/astro
@clerk/backend
@clerk/chrome-extension
@clerk/clerk-js
@clerk/dev-cli
@clerk/expo
@clerk/expo-passkeys
@clerk/express
@clerk/fastify
@clerk/hono
@clerk/localizations
@clerk/nextjs
@clerk/nuxt
@clerk/react
@clerk/react-router
@clerk/shared
@clerk/tanstack-react-start
@clerk/testing
@clerk/ui
@clerk/upgrade
@clerk/vue
commit: |
wobsoriano
left a comment
There was a problem hiding this comment.
left a question, but looks good here
| jobs: | ||
| android: | ||
| name: Android | ||
| runs-on: ubuntu-latest |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
not pushing to use the blacksmith runner, just curious 👀
There was a problem hiding this comment.
Would probably be a good use case, will use the blacksmith runner!
| 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: |
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
.github/workflows/mobile-e2e.yml (1)
16-19:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAdd an explicit top-level
permissions:block.The workflow has no
permissions:declaration, so theGITHUB_TOKENfalls back to the repository default (oftencontents: writeand 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 winSPM 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/DerivedDatacontains 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. Sincenpx expo prebuild --cleanregeneratesios/each run, large parts of this cache will be stale or unusable. For SPM specifically, prefer caching~/Library/Caches/org.swift.swiftpmand theSourcePackages/checkoutssubtree 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 bypackages/expo/package.json. The current key will hit the cache even when SPM deps materially change, and miss when only@clerk/expopackage 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
hashFilesis evaluated for the cache restore phase —actions/cacheevaluateshashFilesat 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
📒 Files selected for processing (1)
.github/workflows/mobile-e2e.yml
| - name: Write quickstart .env | ||
| working-directory: clerk-expo-quickstart/NativeComponentQuickstart | ||
| run: | | ||
| echo "EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY=${{ steps.keys.outputs.pk }}" > .env |
There was a problem hiding this comment.
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" > .envAlso 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.
| 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" |
There was a problem hiding this comment.
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.
|
Merging this to unblock the next PR. |
Summary
Splits the
mobile-e2e.ymlworkflow file out of #8334 so it lands onmainfirst.workflow_dispatchonly 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 againstchris/native-component-testsand validate the full mobile e2e end-to-end before merging the larger PR.The workflow itself is manual-only:
gh workflow run mobile-e2e.yml.INTEGRATION_STAGING_INSTANCE_KEYSrepo secret (entryclerkstage-with-native-components, added separately by the SDK team).The test infrastructure that this workflow runs (Maestro flows under
integration-mobile/, the newpackages/expounit tests, native Swift/Kotlin tests) lives in #8334 and will be merged separately.Test plan
main.clerkstage-with-native-componentsentry to theINTEGRATION_STAGING_INSTANCE_KEYSrepo secret (already in 1Password'sShared/JS SDKs integration testsnote).chris/native-component-testsfrom the Actions UI; confirm bothAndroidandiOSjobs come back green.