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
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"
set +e
maestro test \
+9 -10
View File
@@ -108,16 +108,15 @@ jobs:
google-services-token: ${{ secrets.GOOGLE_SERVICES_TOKEN }}
- name: 🏗️ EAS Build
env:
PROFILE: ${{ inputs.profile || 'testflight-android' }}
run: >
SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_RELEASE=${{ steps.env.outputs.release-version }}
SENTRY_DIST=${{ steps.env.outputs.bundle-identifier }}
pnpm use-build-number-with-bump
pnpm eas build -p android
--profile $PROFILE
--local --output build.aab --non-interactive
uses: ./.github/actions/eas-local-build
with:
platform: android
profile: ${{ inputs.profile || 'testflight-android' }}
output: build.aab
bump-build-number: "true"
sentry-auth-token: ${{ secrets.SENTRY_AUTH_TOKEN }}
sentry-release: ${{ steps.env.outputs.release-version }}
sentry-dist: ${{ steps.env.outputs.bundle-identifier }}
- name: 📚 Get version from package.json
id: get-build-info
+9 -10
View File
@@ -129,16 +129,15 @@ jobs:
google-services-token: ${{ secrets.GOOGLE_SERVICES_TOKEN }}
- name: 🏗️ EAS Build
env:
PROFILE: ${{ inputs.profile || 'testflight' }}
run: >
SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_RELEASE=${{ steps.env.outputs.release-version }}
SENTRY_DIST=${{ steps.env.outputs.bundle-identifier }}
pnpm use-build-number-with-bump
pnpm eas build -p ios
--profile $PROFILE
--local --output build.tar.gz --non-interactive
uses: ./.github/actions/eas-local-build
with:
platform: ios
profile: ${{ inputs.profile || 'testflight' }}
output: build.tar.gz
bump-build-number: "true"
sentry-auth-token: ${{ secrets.SENTRY_AUTH_TOKEN }}
sentry-release: ${{ steps.env.outputs.release-version }}
sentry-dist: ${{ steps.env.outputs.bundle-identifier }}
- name: 📂 Extract build artifact
run: |
+39
View File
@@ -117,6 +117,29 @@ jobs:
xcrun simctl bootstatus "$udid" -b
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
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 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
run: .github/scripts/run-nightly-e2e.sh android emulator-5554
+5 -1
View File
@@ -19,13 +19,17 @@ appId: xyz.blueskyweb.app
when:
platform: Android
commands:
- tapOn: 'http://localhost:8081'
- openLink: "exp+bluesky://expo-development-client/?url=http%3A%2F%2Flocalhost%3A8081"
- runFlow:
label: "Dismiss Expo dev menu"
when:
visible: "Continue"
commands:
- back
- extendedWaitUntil:
visible:
id: e2eProxyHeaderInput
timeout: 60000
- tapOn:
id: e2eProxyHeaderInput
- 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
`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.
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
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"
}
},
"e2e": {
"extends": "development",
"android": {
"buildType": "apk"
},
"env": {
"EXPO_PUBLIC_ENV": "e2e",
"NODE_ENV": "test",
"RN_SRC_EXT": "e2e.ts,e2e.tsx"
}
},
"preview": {
"extends": "base",
"distribution": "internal",