From ce9ccef5154f88b9468b7037fd74057bbbea8135 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Fri, 17 Jul 2026 13:47:31 +0300 Subject: [PATCH] Reuse local EAS builds for nightly E2E --- .github/actions/eas-local-build/action.yml | 72 ++++++++++++++++++++++ .github/scripts/run-nightly-e2e.sh | 26 -------- .github/workflows/build-submit-android.yml | 19 +++--- .github/workflows/build-submit-ios.yml | 19 +++--- .github/workflows/nightly-e2e.yml | 39 ++++++++++++ __e2e__/setupApp.yml | 6 +- docs/testing.md | 3 + eas.json | 11 ++++ 8 files changed, 148 insertions(+), 47 deletions(-) create mode 100644 .github/actions/eas-local-build/action.yml diff --git a/.github/actions/eas-local-build/action.yml b/.github/actions/eas-local-build/action.yml new file mode 100644 index 0000000000..ae62b0d572 --- /dev/null +++ b/.github/actions/eas-local-build/action.yml @@ -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 diff --git a/.github/scripts/run-nightly-e2e.sh b/.github/scripts/run-nightly-e2e.sh index df387ef24c..fedab57e93 100755 --- a/.github/scripts/run-nightly-e2e.sh +++ b/.github/scripts/run-nightly-e2e.sh @@ -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 \ diff --git a/.github/workflows/build-submit-android.yml b/.github/workflows/build-submit-android.yml index e3e01a48f0..ae667701b2 100644 --- a/.github/workflows/build-submit-android.yml +++ b/.github/workflows/build-submit-android.yml @@ -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 diff --git a/.github/workflows/build-submit-ios.yml b/.github/workflows/build-submit-ios.yml index 35d956c62a..5c26977f23 100644 --- a/.github/workflows/build-submit-ios.yml +++ b/.github/workflows/build-submit-ios.yml @@ -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: | diff --git a/.github/workflows/nightly-e2e.yml b/.github/workflows/nightly-e2e.yml index 182337df02..f37fec8f1c 100644 --- a/.github/workflows/nightly-e2e.yml +++ b/.github/workflows/nightly-e2e.yml @@ -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 diff --git a/__e2e__/setupApp.yml b/__e2e__/setupApp.yml index dc784e661f..01ba6703fc 100644 --- a/__e2e__/setupApp.yml +++ b/__e2e__/setupApp.yml @@ -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} diff --git a/docs/testing.md b/docs/testing.md index d265255220..4fde458911 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -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 diff --git a/eas.json b/eas.json index 7ab73ab4c8..5bf4e25513 100644 --- a/eas.json +++ b/eas.json @@ -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",