Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 57 additions & 36 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Written by `e2e init --ci github-actions`: the e2e tests on every pull request
# and every push to main, one job per platform. See https://e2e.com/docs/ci/github-actions
name: e2e

on:
Expand All @@ -14,8 +16,6 @@ concurrency:

jobs:
web:
# Fork pull requests get no secrets, so the agent steps can't call the model.
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
Expand All @@ -24,7 +24,8 @@ jobs:
with:
node-version: 24
cache: npm
- run: npm ci
- name: Install dependencies
run: npm ci
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.cache/ms-playwright
Expand All @@ -33,47 +34,49 @@ jobs:
- name: Install browsers
run: npx e2e-web install chromium --with-deps

# The web target starts Metro itself (app.command in e2e.config.ts).
- name: Run e2e
run: npx e2e run --target web --reporter list,junit
env:
AI_GATEWAY_API_KEY: ${{ secrets.AI_GATEWAY_API_KEY }}

- name: Upload report
- name: Upload the report
if: ${{ !cancelled() }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: e2e-web
path: .e2e
if-no-files-found: warn
# .e2e is a hidden directory, which upload-artifact skips by default.
path: |
.e2e/report.json
.e2e/junit.xml
.e2e/results
# .e2e is a hidden directory, which upload-artifact skips when it expands a folder.
include-hidden-files: true
if-no-files-found: warn
retention-days: 7

ios:
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
runs-on: macos-26
# The Release build alone takes about 35 minutes on this runner.
# A Release build for the simulator alone can take 35 minutes on this runner.
timeout-minutes: 90
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24
cache: npm
- run: npm ci
- name: Install dependencies
run: npm ci

# ios/ is generated (CNG); expo run:ios prebuilds it, installs pods, and builds without a simulator.
- name: Build for the simulator
# Expo generates ios/ (CNG): expo run:ios prebuilds it, installs the pods, and builds without a simulator.
- name: Build the app
run: npx expo run:ios --configuration Release --device generic --no-bundler --output build/ios
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.agent-device/apple-runner/derived
key: agent-device-runner-${{ runner.os }}-${{ hashFiles('package-lock.json') }}

# Boots the image's own iPhone 17 Pro on the pinned runtime: a simulator made with
# `simctl create` spent over 7 minutes on its first boot. Bump the runtime on purpose,
# after a local run on it, since the accessibility tree changes between iOS releases.
# Boots the image's own iPhone 17 Pro on a pinned runtime: a simulator made with `simctl create`
# can spend minutes on its first boot. Bump the runtime on purpose, after a local run on it,
# since the accessibility tree changes between iOS releases.
- name: Boot a simulator and install the app
env:
IOS_RUNTIME: com.apple.CoreSimulator.SimRuntime.iOS-26-5
Expand All @@ -87,29 +90,35 @@ jobs:
fi
xcrun simctl boot "$UDID"
xcrun simctl bootstatus "$UDID"
APP=$(find build/ios -maxdepth 3 -name 'e2ereactnative.app' -type d | head -n 1)
test -n "$APP"
echo "Installing $APP"
APP=$(find build -maxdepth 5 -name '*.app' -type d | head -n 1)
if [ -z "$APP" ]; then
echo "::error::No .app under build/"
exit 1
fi
xcrun simctl install "$UDID" "$APP"
echo "E2E_IOS_DEVICE=$UDID" >> "$GITHUB_ENV"
# A target with device: process.env.E2E_DEVICE drives this simulator, whatever else the image has.
echo "E2E_DEVICE=$UDID" >> "$GITHUB_ENV"

- name: Run e2e
run: npx e2e run --target ios --workers 1 --reporter list,junit
env:
AI_GATEWAY_API_KEY: ${{ secrets.AI_GATEWAY_API_KEY }}

- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
- name: Upload the report
if: ${{ !cancelled() }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: e2e-ios
path: .e2e
if-no-files-found: warn
# .e2e is a hidden directory, which upload-artifact skips by default.
path: |
.e2e/report.json
.e2e/junit.xml
.e2e/results
# .e2e is a hidden directory, which upload-artifact skips when it expands a folder.
include-hidden-files: true
if-no-files-found: warn
retention-days: 7

android:
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
Expand All @@ -122,34 +131,40 @@ jobs:
with:
distribution: temurin
java-version: 17
- run: npm ci
- name: Install dependencies
run: npm ci

- name: Enable KVM
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm

# android/ is generated (CNG). The Expo template signs release builds with the debug keystore.
# Only the emulator's ABI: building all four takes about 25 minutes.
- name: Build the APK
# Expo generates android/ (CNG) and signs release builds with the debug keystore.
# Only the emulator's ABI: building all four takes about 25 minutes longer.
- name: Build the app
run: |
npx expo prebuild --platform android --no-install
cd android && ./gradlew :app:assembleRelease -PreactNativeArchitectures=x86_64
(cd android && ./gradlew :app:assembleRelease -PreactNativeArchitectures=x86_64)

# The emulator action downloads the system image once and fails the job when that download drops.
# sdkmanager can also report success for a package that holds no image, so the step checks for one.
- name: Install the emulator system image
run: |
SDKMANAGER="$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager"
IMAGE="$ANDROID_HOME/system-images/android-36/google_apis/x86_64"
"$SDKMANAGER" --licenses < <(yes) > /dev/null
for attempt in 1 2 3; do
"$SDKMANAGER" --install 'system-images;android-36;google_apis;x86_64' > /dev/null && exit 0
if "$SDKMANAGER" --install 'system-images;android-36;google_apis;x86_64' > /dev/null && ls "$IMAGE"/*.img > /dev/null 2>&1; then
exit 0
fi
echo "Download attempt $attempt failed"
rm -rf "$IMAGE"
sleep 30
done
exit 1

- name: Run on an emulator
- name: Run e2e
uses: reactivecircus/android-emulator-runner@a421e43855164a8197daf9d8d40fe71c6996bb0d # v2.38.0
with:
api-level: 36
Expand All @@ -163,13 +178,19 @@ jobs:
npx e2e run --target android --workers 1 --reporter list,junit
env:
AI_GATEWAY_API_KEY: ${{ secrets.AI_GATEWAY_API_KEY }}
# The emulator the action boots, for a target with device: process.env.E2E_DEVICE.
E2E_DEVICE: emulator-5554

- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
- name: Upload the report
if: ${{ !cancelled() }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: e2e-android
path: .e2e
if-no-files-found: warn
# .e2e is a hidden directory, which upload-artifact skips by default.
path: |
.e2e/report.json
.e2e/junit.xml
.e2e/results
# .e2e is a hidden directory, which upload-artifact skips when it expands a folder.
include-hidden-files: true
if-no-files-found: warn
retention-days: 7
2 changes: 1 addition & 1 deletion e2e.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export default {
{
name: 'ios',
// CI passes the UDID of a simulator it created on a pinned iOS runtime.
engine: mobile({ platform: 'ios', device: process.env.E2E_IOS_DEVICE || 'iPhone 17 Pro' }),
engine: mobile({ platform: 'ios', device: process.env.E2E_DEVICE || 'iPhone 17 Pro' }),
// A development build made with `npx expo run:ios`; Metro serves the JS bundle.
app: {
bundleId: 'dev.e2e.podcasts',
Expand Down
Loading