Reuse local EAS builds for nightly E2E

This commit is contained in:
Samuel Newman
2026-07-17 13:47:31 +03:00
parent 822e803da2
commit ce9ccef515
8 changed files with 148 additions and 47 deletions
@@ -0,0 +1,72 @@
---
name: Local EAS Build
description: Build an Expo app locally with a selected EAS profile.
inputs:
platform:
description: EAS platform to build (ios or android)
required: true
profile:
description: EAS build profile
required: true
output:
description: Output path for the local build artifact
required: true
log-path:
description: Optional path to tee build output into
required: false
default: ""
bump-build-number:
description: Run the build through use-build-number-with-bump
required: false
default: "false"
sentry-auth-token:
description: Optional Sentry authentication token
required: false
default: ""
sentry-release:
description: Optional Sentry release
required: false
default: ""
sentry-dist:
description: Optional Sentry distribution
required: false
default: ""
runs:
using: composite
steps:
- name: Build locally with EAS
shell: bash
env:
PLATFORM: ${{ inputs.platform }}
PROFILE: ${{ inputs.profile }}
OUTPUT: ${{ inputs.output }}
LOG_PATH: ${{ inputs.log-path }}
BUMP_BUILD_NUMBER: ${{ inputs.bump-build-number }}
SENTRY_AUTH_TOKEN: ${{ inputs.sentry-auth-token }}
SENTRY_RELEASE: ${{ inputs.sentry-release }}
SENTRY_DIST: ${{ inputs.sentry-dist }}
run: |
set -o pipefail
build_command=(
pnpm eas build
--platform "$PLATFORM"
--profile "$PROFILE"
--local
--output "$OUTPUT"
--non-interactive
)
if [ -n "$LOG_PATH" ]; then
mkdir -p "$(dirname "$LOG_PATH")"
if [ "$BUMP_BUILD_NUMBER" = "true" ]; then
pnpm use-build-number-with-bump "${build_command[@]}" 2>&1 | tee "$LOG_PATH"
else
"${build_command[@]}" 2>&1 | tee "$LOG_PATH"
fi
elif [ "$BUMP_BUILD_NUMBER" = "true" ]; then
pnpm use-build-number-with-bump "${build_command[@]}"
else
"${build_command[@]}"
fi
-26
View File
@@ -142,32 +142,6 @@ if [[ "$platform" == "android" ]]; then
adb -s "$device_id" reverse tcp:8081 tcp:8081 adb -s "$device_id" reverse tcp:8081 tcp:8081
fi fi
phase "Building and installing the development client"
if [[ "$platform" == "ios" ]]; then
# Passing --device makes this Expo version merge simulator and devicectl
# results. On Xcode 26.4, devicectl can misclassify the simulator as a
# physical device and incorrectly require signing. Exactly one simulator is
# booted above, so Expo's default simulator-only resolver selects it.
echo "Building for already booted iOS simulator $device_id"
EXPO_PUBLIC_ENV=e2e \
NODE_ENV=test \
RN_SRC_EXT=e2e.ts,e2e.tsx \
pnpm exec expo run:ios --no-bundler \
2>&1 | tee "$artifact_dir/build.log"
else
expo_device_name="$(adb -s "$device_id" emu avd name | sed -n '1p' | tr -d '\r')"
if [[ -z "$expo_device_name" ]]; then
echo "Could not resolve the AVD name for $device_id" >&2
exit 1
fi
echo "Building for Android AVD $expo_device_name ($device_id)"
EXPO_PUBLIC_ENV=e2e \
NODE_ENV=test \
RN_SRC_EXT=e2e.ts,e2e.tsx \
pnpm exec expo run:android --device "$expo_device_name" --no-bundler \
2>&1 | tee "$artifact_dir/build.log"
fi
phase "Running Maestro flows" phase "Running Maestro flows"
set +e set +e
maestro test \ maestro test \
+9 -10
View File
@@ -108,16 +108,15 @@ jobs:
google-services-token: ${{ secrets.GOOGLE_SERVICES_TOKEN }} google-services-token: ${{ secrets.GOOGLE_SERVICES_TOKEN }}
- name: 🏗️ EAS Build - name: 🏗️ EAS Build
env: uses: ./.github/actions/eas-local-build
PROFILE: ${{ inputs.profile || 'testflight-android' }} with:
run: > platform: android
SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }} profile: ${{ inputs.profile || 'testflight-android' }}
SENTRY_RELEASE=${{ steps.env.outputs.release-version }} output: build.aab
SENTRY_DIST=${{ steps.env.outputs.bundle-identifier }} bump-build-number: "true"
pnpm use-build-number-with-bump sentry-auth-token: ${{ secrets.SENTRY_AUTH_TOKEN }}
pnpm eas build -p android sentry-release: ${{ steps.env.outputs.release-version }}
--profile $PROFILE sentry-dist: ${{ steps.env.outputs.bundle-identifier }}
--local --output build.aab --non-interactive
- name: 📚 Get version from package.json - name: 📚 Get version from package.json
id: get-build-info id: get-build-info
+9 -10
View File
@@ -129,16 +129,15 @@ jobs:
google-services-token: ${{ secrets.GOOGLE_SERVICES_TOKEN }} google-services-token: ${{ secrets.GOOGLE_SERVICES_TOKEN }}
- name: 🏗️ EAS Build - name: 🏗️ EAS Build
env: uses: ./.github/actions/eas-local-build
PROFILE: ${{ inputs.profile || 'testflight' }} with:
run: > platform: ios
SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }} profile: ${{ inputs.profile || 'testflight' }}
SENTRY_RELEASE=${{ steps.env.outputs.release-version }} output: build.tar.gz
SENTRY_DIST=${{ steps.env.outputs.bundle-identifier }} bump-build-number: "true"
pnpm use-build-number-with-bump sentry-auth-token: ${{ secrets.SENTRY_AUTH_TOKEN }}
pnpm eas build -p ios sentry-release: ${{ steps.env.outputs.release-version }}
--profile $PROFILE sentry-dist: ${{ steps.env.outputs.bundle-identifier }}
--local --output build.tar.gz --non-interactive
- name: 📂 Extract build artifact - name: 📂 Extract build artifact
run: | run: |
+39
View File
@@ -117,6 +117,29 @@ jobs:
xcrun simctl bootstatus "$udid" -b xcrun simctl bootstatus "$udid" -b
echo "Using $device_name on $runtime_name ($udid)" echo "Using $device_name on $runtime_name ($udid)"
- name: Mark iOS development client build phase
run: echo "Building the iOS development client" > artifacts/ios/phase.txt
- name: Build iOS development client
uses: ./.github/actions/eas-local-build
with:
platform: ios
profile: e2e
output: ${{ runner.temp }}/nightly-e2e-ios.tar.gz
log-path: artifacts/ios/build.log
- name: Install iOS development client
run: |
build_contents="$RUNNER_TEMP/nightly-e2e-ios-build"
mkdir -p "$build_contents"
tar -xzf "$RUNNER_TEMP/nightly-e2e-ios.tar.gz" -C "$build_contents"
app_path=$(find "$build_contents" -type d -name '*.app' -print -quit)
if [ -z "$app_path" ]; then
echo "The local EAS build did not contain an iOS simulator app" >&2
exit 1
fi
xcrun simctl install "$IOS_UDID" "$app_path" 2>&1 | tee -a artifacts/ios/build.log
- name: Run iOS Maestro suite - name: Run iOS Maestro suite
run: .github/scripts/run-nightly-e2e.sh ios "$IOS_UDID" run: .github/scripts/run-nightly-e2e.sh ios "$IOS_UDID"
@@ -252,6 +275,22 @@ jobs:
adb -s emulator-5554 shell settings put global transition_animation_scale 0 adb -s emulator-5554 shell settings put global transition_animation_scale 0
adb -s emulator-5554 shell settings put global animator_duration_scale 0 adb -s emulator-5554 shell settings put global animator_duration_scale 0
- name: Mark Android development client build phase
run: echo "Building the Android development client" > artifacts/android/phase.txt
- name: Build Android development client
uses: ./.github/actions/eas-local-build
with:
platform: android
profile: e2e
output: ${{ runner.temp }}/nightly-e2e-android.apk
log-path: artifacts/android/build.log
- name: Install Android development client
run: |
adb -s emulator-5554 install -r "$RUNNER_TEMP/nightly-e2e-android.apk" \
2>&1 | tee -a artifacts/android/build.log
- name: Run Android Maestro suite - name: Run Android Maestro suite
run: .github/scripts/run-nightly-e2e.sh android emulator-5554 run: .github/scripts/run-nightly-e2e.sh android emulator-5554
+5 -1
View File
@@ -19,13 +19,17 @@ appId: xyz.blueskyweb.app
when: when:
platform: Android platform: Android
commands: commands:
- tapOn: 'http://localhost:8081' - openLink: "exp+bluesky://expo-development-client/?url=http%3A%2F%2Flocalhost%3A8081"
- runFlow: - runFlow:
label: "Dismiss Expo dev menu" label: "Dismiss Expo dev menu"
when: when:
visible: "Continue" visible: "Continue"
commands: commands:
- back - back
- extendedWaitUntil:
visible:
id: e2eProxyHeaderInput
timeout: 60000
- tapOn: - tapOn:
id: e2eProxyHeaderInput id: e2eProxyHeaderInput
- inputText: ${output.result} - inputText: ${output.result}
+3
View File
@@ -42,6 +42,9 @@ used. iOS runs on `macos-26-xlarge` with Xcode 26.4. Android runs on
`Linux-x64-32core`. Both use Java 17 and the Node and pnpm versions declared in `Linux-x64-32core`. Both use Java 17 and the Node and pnpm versions declared in
`package.json`. The iOS job selects an iPhone 17 simulator running iOS 26.5; `package.json`. The iOS job selects an iPhone 17 simulator running iOS 26.5;
Android uses a Pixel 6 AVD with the API 35 Google APIs x86_64 image. Android uses a Pixel 6 AVD with the API 35 Google APIs x86_64 image.
Both development clients use the `e2e` EAS profile and the same reusable local
EAS build action as the release build workflows; the resulting simulator app
and APK are installed directly on the selected devices.
The mock-server manager listens on host port 1986 and creates test services on The mock-server manager listens on host port 1986 and creates test services on
port 3000. Metro listens on 8081. Android reverses ports 3000 and 8081 into the port 3000. Metro listens on 8081. Android reverses ports 3000 and 8081 into the
+11
View File
@@ -21,6 +21,17 @@
"EXPO_PUBLIC_ENV": "production" "EXPO_PUBLIC_ENV": "production"
} }
}, },
"e2e": {
"extends": "development",
"android": {
"buildType": "apk"
},
"env": {
"EXPO_PUBLIC_ENV": "e2e",
"NODE_ENV": "test",
"RN_SRC_EXT": "e2e.ts,e2e.tsx"
}
},
"preview": { "preview": {
"extends": "base", "extends": "base",
"distribution": "internal", "distribution": "internal",