[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/pythonnative/pythonnative/main/.github/workflows/e2e.yml [Back]  [Original]

name: E2E

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  workflow_dispatch:

jobs:
  coverage:
    # Runs first; fast static check that gates the long device jobs so
    # missing demos / flows fail before the emulator boots.
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.13'
      - name: Check E2E coverage of pythonnative.__all__
        run: python scripts/check-e2e-coverage.py

  e2e-android:
    needs: coverage
    runs-on: ubuntu-latest
    timeout-minutes: 30
    # GitHub-hosted Android emulators grow unstable after roughly ten
    # minutes of sustained Maestro driving and start reporting "device
    # offline" / "device not found" mid-run (the emulator VM, not the
    # app, dies). Sharding the run into a few balanced groups, each on
    # its own freshly booted emulator, keeps every session under the
    # limit, and parallelizes the Android pass as a bonus. Groups are
    # sized to about nine minutes of driving or less: ``components``
    # (39 flows) runs as three parts; ``hooks`` (26 flows) dropped
    # offline at ~11.5 minutes in one session, so it runs as two parts;
    # the remaining categories pair up to similar totals, and ``misc``
    # (14 flows) runs alone.
    # ``fail-fast: false`` so one shard's failure still lets the others
    # report.
    strategy:
      fail-fast: false
      matrix:
        include:
          - name: components-a
            suites: components-a
          - name: components-b
            suites: components-b
          - name: components-c
            suites: components-c
          - name: hooks-a
            suites: hooks-a
          - name: hooks-b
            suites: hooks-b
          - name: nav-gestures
            suites: navigation gestures
          - name: layout-styling
            suites: layout styling
          - name: anim-assets
            suites: animations assets
          - name: misc
            suites: misc
          - name: inbox
            suites: inbox

    name: e2e-android (${{ matrix.name }})

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up uv
        uses: astral-sh/setup-uv@v10.0.1
        with:
          enable-cache: true
          python-version: '3.13'

      - name: Set up Java 17
        uses: actions/setup-java@v4
        with:
          distribution: 'temurin'
          java-version: '17'

      # Restores ~/.gradle (wrapper distribution and dependency caches)
      # so the staged project's ./gradlew doesn't download Gradle from
      # services.gradle.org on every shard; a reset connection there
      # fails the build before Maestro even starts.
      - name: Set up Gradle
        uses: gradle/actions/setup-gradle@v4

      - name: Install PythonNative
        run: uv sync --locked

      - name: Install Maestro
        run: |
          curl -Ls "https://get.maestro.mobile.dev" | bash
          echo "$HOME/.maestro/bin" >> $GITHUB_PATH

      - 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

      # The emulator step runs its script through a login shell, which
      # sources /etc/profile and can rewrite PATH. Confirm uv survives that
      # here, in seconds, rather than after a multi-minute emulator boot.
      - name: Verify uv is reachable in a login shell
        run: bash -lc 'command -v uv && uv --version'

      - name: Build, install, and run E2E tests
        uses: reactivecircus/android-emulator-runner@v2
        with:
          api-level: 31
          arch: x86_64
          script: bash -lc "uv run ./scripts/run-e2e.sh android ${{ matrix.suites }}"

      # Maestro writes per-flow debug output (command log, view-hierarchy
      # dumps, failure screenshots) under ~/.maestro/tests. Surface it when
      # a shard fails so CI-only failures are diagnosable without guesswork.
      - name: Upload Maestro debug output
        if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: maestro-debug-android-${{ matrix.name }}
          path: ~/.maestro/tests/
          if-no-files-found: ignore
          retention-days: 7

  e2e-ios:
    needs: coverage
    # Pinned to macos-15 rather than macos-latest: the macos-26 image
    # (default since mid-July 2026) sporadically delivers a single
    # XCTest-synthesized tap as TWO UIControl action sends (~25% of
    # taps; the app's unified log shows one UIEvent followed by a
    # doubled "send control actions" burst ~1ms apart). It reproduces
    # on both the iOS 26.5 and 26.2 simulator runtimes on that image,
    # but never on a macOS 15 host, so the host-side simulator/XCTest
    # event-injection stack is the trigger. Doubled taps break every
    # non-idempotent press handler: reducer counters jump by two,
    # alerts present twice, pickers reopen after selecting. This suite
    # ran green on the macos-15 image for months.
    runs-on: macos-15
    env:
      DEVELOPER_DIR: /Applications/Xcode_26.3.app/Contents/Developer
    # Budget for the worst case: ~8 min build/boot plus two full suite
    # attempts (run-e2e.sh retries the whole suite once after a driver
    # flake). The test step gets its own, shorter timeout so a hung
    # attempt still leaves time for the debug-output upload below;
    # a job-level timeout kills the runner before that step can run.
    timeout-minutes: 55
    # iOS simulators are stable for the full suite (unlike the Android
    # emulator; see the e2e-android note), so this split is purely a
    # speed optimization: the iOS Maestro run is ~22 minutes and is the
    # critical path for the whole E2E workflow. Sharding it across
    # simulators (same balanced groups as Android) runs them in parallel
    # and roughly halves wall-clock time. macOS minutes are free on this
    # public repo, so the extra per-shard build/boot overhead is fine.
    # Shards are sized so a single attempt is ~10-12 minutes of driving:
    # ``components`` alone was ~21 minutes per attempt on a hosted
    # runner, so one retry (build + two attempts) overran the job budget.
    # ``fail-fast: false`` so one shard's failure still lets the others
    # report.
    strategy:
      fail-fast: false
      matrix:
        include:
          - name: components-a
            suites: components-a
          - name: components-b
            suites: components-b
          - name: components-c
            suites: components-c
          - name: hooks
            suites: hooks
          - name: nav-gestures-layout-styling
            suites: navigation gestures layout styling
          - name: anim-misc
            suites: animations assets misc
          - name: inbox
            suites: inbox

    name: e2e-ios (${{ matrix.name }})

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up uv
        uses: astral-sh/setup-uv@v10.0.1
        with:
          enable-cache: true
          python-version: '3.13'

      - name: Install PythonNative
        run: uv sync --locked

      # Maestro drives iOS simulators with its own bundled XCTest
      # runner; idb-companion (once installed here) hasn't been needed
      # since Maestro dropped idb, and its formula no longer installs
      # on macos-15 anyway (no bottle, and building needs Xcode 26).
      - name: Install Maestro
        run: |
          curl -Ls "https://get.maestro.mobile.dev" | bash
          echo "$HOME/.maestro/bin" >> $GITHUB_PATH

      - name: Build and run E2E tests
        timeout-minutes: 48
        run: uv run ./scripts/run-e2e.sh ios ${{ matrix.suites }}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      # Maestro writes per-flow debug output (command log, view-hierarchy
      # dumps, failure screenshots) under ~/.maestro/tests. Surface it when
      # a shard fails (or hits the step timeout above) so CI-only failures
      # are diagnosable without guesswork.
      - name: Upload Maestro debug output
        if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: maestro-debug-ios-${{ matrix.name }}
          path: ~/.maestro/tests/
          if-no-files-found: ignore
          retention-days: 7

Web Proxy Viewer  |  New URL  |  Original Page