mirror of
https://github.com/block/buzz.git
synced 2026-10-27 04:30:52 +00:00
perf(mobile): cut test wall time and run mobile CI lanes in parallel (#8347)
## Problem Mobile pushes and PRs are slow. - **Pre-push takes ~133s on a mobile-only change.** About 115s of that is `flutter test`, and the finish time is set by a few files that wait on real clocks. - `push_presentation_export_test` (75s) and `push_presentation_recovery_test` (19s) sleep through the full 7.75s retry backoff. - `channel_messages_provider_test` (70s) sleeps through recount and ownership retry backoff on real time. - **The required Mobile CI job takes ~15–16 min.** It runs tests (390–430s on a 4-vCPU runner, 2 test files at a time) and then the Android debug APK build (~400s, no Gradle cache) back to back. - **Mobile Swift takes 18–21 min.** The unsigned release build (~4.5 min) waits behind the simulator tests. Pre-commit is ~3s and isn't part of the problem. ## Change **Tests** (each one still asserts what it did before) - **Push exports.** Production retries wait through `debugPushPresentationRetryWait`, which defaults to `Future.delayed`. The export and recovery tests skip the real waits and assert the exact 250ms → 4s backoff schedule instead. Export fixtures sign each event once per file. - **`channel_messages_provider_test`.** Every test runs on `fakeAsync`. A file-local `test` wrapper keeps the 90 call sites unchanged. Timers fire in their real order, and a test that waits on work outside fake time fails instead of hanging. Raising the production retry bound (`attempt < 2` → `3`) still fails the 5 exhausted-retry tests. - **`signed qualified caller`.** This test mounts the page 48 times and opens the profile sheet twice per mount. It now disables the sheet animation; it asserts which pubkey and labels show, not motion. **CI** - **Mobile runs as five parallel matrix lanes:** checks (format, analyze, unconfigured push test, recipe test), three `flutter test --total-shards=3` shards, and the Android build. The required `Mobile` gate still reads the combined result. - **Android restores a Gradle cache,** which is saved only on pushes to `main`. - **Mobile Swift builds the unsigned release** in a parallel lane beside the native build, tests and simulator checks. ## Validation Measured locally on an 18-core Mac with warm caches: | | Before | After | |---|---|---| | `flutter test` (full suite) | 115s | 74s | | Summed per-test time | 411s | 307s | | `push_presentation_export_test` | 75s | 38s | | `channel_messages_provider_test` | 70s | 22s | | Pre-push, mobile-only push | 133s | 95s | - The CI lane timings come from this PR's own run. - `actionlint` passes, and so do `scripts/test-ci-required-context-isolation.sh` and `scripts/ci-required-gates.test.mjs`. - The pre-push hook (format, analyze, full `flutter test`) passed on push. ## Not in this PR - `channel_detail_page_test` (~55s) is now the local long pole. Splitting it into shard files would let it run in parallel, but it is one 16k-line library with `part` files, so that is a separate refactor. - Caching Hermit and pub on macOS would cut ~100s of setup from each Mobile Swift lane. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <32a2e2c9d428ee08902cab75d956da2c1d235a22d4766b0dd4138bf6e2e5db1d@buzz.block.builderlab.xyz> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
67
.github/workflows/_ci-clients.yml
vendored
67
.github/workflows/_ci-clients.yml
vendored
@@ -57,11 +57,31 @@ jobs:
|
||||
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
||||
key: pnpm-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
# Mobile lanes run in parallel; the required check reads the combined
|
||||
# matrix result. `flutter test` shards split tests, not files, so every test
|
||||
# shard still compiles the whole suite.
|
||||
mobile:
|
||||
name: Mobile
|
||||
name: Mobile (${{ matrix.name }})
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
if: inputs.lane == 'required' && (github.event_name == 'push' || inputs.mobile)
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- name: Checks
|
||||
lane: checks
|
||||
- name: Tests 1/3
|
||||
lane: test
|
||||
shard: 0
|
||||
- name: Tests 2/3
|
||||
lane: test
|
||||
shard: 1
|
||||
- name: Tests 3/3
|
||||
lane: test
|
||||
shard: 2
|
||||
- name: Android
|
||||
lane: android
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
@@ -107,39 +127,79 @@ jobs:
|
||||
path: ~/.pub-cache
|
||||
key: pub-${{ runner.os }}-${{ hashFiles('mobile/pubspec.lock') }}
|
||||
- name: Format check
|
||||
if: matrix.lane == 'checks'
|
||||
run: cd mobile && dart format --output=none --set-exit-if-changed .
|
||||
- name: Analyze
|
||||
if: matrix.lane == 'checks'
|
||||
run: cd mobile && flutter analyze
|
||||
- name: Test
|
||||
run: cd mobile && flutter test --dart-define=BUZZ_PUSH_GATEWAY_URL=https://push.example
|
||||
if: matrix.lane == 'test'
|
||||
run: cd mobile && flutter test --dart-define=BUZZ_PUSH_GATEWAY_URL=https://push.example --total-shards=3 --shard-index=${{ matrix.shard }}
|
||||
- name: Test mobile without push configuration
|
||||
if: matrix.lane == 'checks'
|
||||
run: cd mobile && flutter test test/shared/push/push_unconfigured_build_test.dart
|
||||
- name: Test mobile recipe argument forwarding
|
||||
if: matrix.lane == 'checks'
|
||||
run: /bin/bash scripts/test-mobile-gateway-recipes.sh
|
||||
- name: Restore Gradle cache
|
||||
if: matrix.lane == 'android'
|
||||
uses: actions/cache/restore@caa296126883cff596d87d8935842f9db880ef25 # v5
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
key: gradle-${{ runner.os }}-${{ hashFiles('mobile/android/**/*.gradle*', 'mobile/android/gradle/wrapper/gradle-wrapper.properties', 'mobile/pubspec.lock') }}
|
||||
restore-keys: gradle-${{ runner.os }}-
|
||||
- name: Build Android debug APK
|
||||
if: matrix.lane == 'android'
|
||||
run: just mobile-build-android
|
||||
- name: Save Gradle cache
|
||||
if: matrix.lane == 'android' && github.event_name == 'push'
|
||||
uses: actions/cache/save@caa296126883cff596d87d8935842f9db880ef25 # v5
|
||||
continue-on-error: true
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
key: gradle-${{ runner.os }}-${{ hashFiles('mobile/android/**/*.gradle*', 'mobile/android/gradle/wrapper/gradle-wrapper.properties', 'mobile/pubspec.lock') }}
|
||||
|
||||
# The unsigned release build needs nothing from the native lane, so it runs
|
||||
# beside it instead of after the simulator tests.
|
||||
mobile-swift:
|
||||
name: Mobile Swift
|
||||
name: Mobile Swift (${{ matrix.name }})
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 45
|
||||
if: inputs.lane == 'mobile-swift' && inputs.mobile
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- name: Native
|
||||
lane: native
|
||||
- name: Release
|
||||
lane: release
|
||||
steps:
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
- uses: cashapp/activate-hermit@cea9af7913204a965fd488637a8d1811bba2e616 # v1
|
||||
- name: Install Flutter dependencies
|
||||
run: cd mobile && flutter pub get
|
||||
- name: Build
|
||||
if: matrix.lane == 'native'
|
||||
run: swift build --package-path mobile/ios/BuzzPushKit
|
||||
- name: Build release
|
||||
if: matrix.lane == 'native'
|
||||
run: swift build -c release --package-path mobile/ios/BuzzPushKit
|
||||
- name: Test
|
||||
if: matrix.lane == 'native'
|
||||
run: swift test --package-path mobile/ios/BuzzPushKit
|
||||
- name: Test native pagination geometry
|
||||
if: matrix.lane == 'native'
|
||||
run: /bin/bash scripts/test-mobile-pagination.sh
|
||||
- name: Build complete iOS simulator app
|
||||
if: matrix.lane == 'native'
|
||||
run: cd mobile && flutter build ios --simulator --debug --no-pub
|
||||
- name: Test native controls and push navigation bridge
|
||||
if: matrix.lane == 'native'
|
||||
run: |
|
||||
device=$(xcrun simctl create "Pagination accessibility" "iPhone 16")
|
||||
trap 'xcrun simctl shutdown "$device"; xcrun simctl delete "$device"' EXIT
|
||||
@@ -153,6 +213,7 @@ jobs:
|
||||
-only-testing:RunnerTests/BuzzPushNavigationBridgeTests \
|
||||
CODE_SIGNING_ALLOWED=NO test
|
||||
- name: Build complete unsigned iOS release
|
||||
if: matrix.lane == 'release'
|
||||
run: |
|
||||
/bin/bash scripts/test-mobile-gateway-recipes.sh
|
||||
cd mobile && flutter build ios --release --no-codesign --no-pub
|
||||
|
||||
Reference in New Issue
Block a user