name: Compatibility Matrix Testing on: workflow_dispatch: inputs: CMT_MATRIX: description: "A JSON object representing the testing matrix" required: true type: string MOBILE_VERSION: description: "The mattermost mobile version to test" required: true # Cancel overlapping CMT runs for the same ref; matterwick cleans up cancelled servers via workflow_run completed. concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true # CMT runs against a freshly-cut build-release-NNN branch, so the app content # never matches main's fingerprint — the S3 build cache would always miss. # CMT's optimization is structural instead: each app (iOS, Android Detox, # Android Maestro) is built ONCE below and reused across every server-version # leg of the matrix via github.run_id artifacts. jobs: calculate-commit-hash: runs-on: ubuntu-24.04 outputs: MOBILE_SHA: ${{ steps.repo.outputs.MOBILE_SHA }} steps: - name: cmt/checkout-mobile uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false ref: ${{ inputs.MOBILE_VERSION }} - name: cmt/calculate-mattermost-sha id: repo run: echo "MOBILE_SHA=$(git rev-parse HEAD)" >> "${GITHUB_OUTPUT}" # Phase 5 — base TSIO identity for this CMT run. Each matrix leg builds its # own per-job report group (total_reports_expected=1) from this base + tsio-shard-name. cmt-identity: name: Calculate CMT TSIO base identity runs-on: ubuntu-24.04 permissions: contents: read needs: - calculate-commit-hash - prepare-maestro-matrix outputs: tsio-base-config: ${{ steps.id.outputs.tsio-base-config }} tsio-job-configs: ${{ steps.id.outputs.tsio-job-configs }} steps: - name: Sparse checkout TSIO job-config helper uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: ref: ${{ inputs.MOBILE_VERSION }} persist-credentials: false sparse-checkout: | detox/utils/build-tsio-job-config.js - id: id env: GITHUB_REPOSITORY: ${{ github.repository }} MM_SHA: ${{ needs.calculate-commit-hash.outputs.MOBILE_SHA }} MOBILE_VERSION: ${{ inputs.MOBILE_VERSION }} CMT_MATRIX: ${{ inputs.CMT_MATRIX }} MAESTRO_MATRIX: ${{ needs.prepare-maestro-matrix.outputs.matrix }} run: | BRANCH=$(echo "$MOBILE_VERSION" | sed 's#^refs/heads/##; s#^refs/tags/##') BASE=$(jq -nc \ --arg repo "$GITHUB_REPOSITORY" --arg sha "$MM_SHA" --arg run_id "$GITHUB_RUN_ID" \ --arg attempt "$GITHUB_RUN_ATTEMPT" --arg branch "$BRANCH" \ '{repository:$repo, commit_sha:$sha, gh_run_id:$run_id, gh_run_attempt:$attempt, branch:$branch, name:"mobile-release", run_group:"mobile-release", framework:"detox"}') # No status_context — templates expand per tsio-shard-name via buildTsioJobConfig. TSIO_BASE=$(jq -nc --argjson identity "$BASE" \ '{composite_identity: $identity, total_reports_expected: 1}') echo "tsio-base-config=$TSIO_BASE" >> "$GITHUB_OUTPUT" # Per-shard configs so the final rollup can poll every leg's TSIO group. export BASE CMT_MATRIX MAESTRO_MATRIX JOB_CONFIGS=$(node -e ' const {buildTsioJobConfigMap, cmtJobKeys} = require("./detox/utils/build-tsio-job-config.js"); const base = JSON.parse(process.env.BASE); const keys = cmtJobKeys(JSON.parse(process.env.CMT_MATRIX), JSON.parse(process.env.MAESTRO_MATRIX)); process.stdout.write(JSON.stringify(buildTsioJobConfigMap(base, keys))); ') echo "tsio-job-configs=$JOB_CONFIGS" >> "$GITHUB_OUTPUT" update-initial-status: runs-on: ubuntu-24.04 permissions: contents: read statuses: write needs: - calculate-commit-hash - cmt-identity steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: ref: ${{ inputs.MOBILE_VERSION }} persist-credentials: false # Pending statuses are posted per matrix leg when each job starts; keep a # single umbrella pending only when MOBILE_SHA is known so the PR/commit # UI shows CMT is running before the first shard uploads. - name: Build TSIO report URL (release group prefix) id: tsio-url env: TSIO_BASE: ${{ needs.cmt-identity.outputs.tsio-base-config }} run: | URL=$(jq -nr --argjson cfg "$TSIO_BASE" --arg base "https://test-io.test.mattermost.com" ' ($cfg.composite_identity) as $id | ($id.repository | split("/") | .[-1]) as $repo | ($id.branch // "main" | sub("^refs/heads/"; "") | sub("^refs/tags/"; "") | gsub("/"; "~")) as $branch | ($id.commit_sha[0:7]) as $sha | "\($base)/reports/\($repo|@uri)/\($branch|@uri)/\($sha)/mobile-release?gh_run_id=\($id.gh_run_id)" ') echo "target_url=$URL" >> "$GITHUB_OUTPUT" - uses: ./.github/actions/update-commit-status if: ${{ needs.calculate-commit-hash.outputs.MOBILE_SHA != '' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: repository_full_name: ${{ github.repository }} commit_sha: ${{ needs.calculate-commit-hash.outputs.MOBILE_SHA }} context: e2e-test/compatibility-matrix-testing description: "Compatibility Matrix Testing for ${{ inputs.MOBILE_VERSION }} version — per-job checks also posted" status: pending target_url: ${{ steps.tsio-url.outputs.target_url }} # Maestro only runs against the latest server version; matrix filtering happens here since job-level if: can't access matrix. prepare-maestro-matrix: runs-on: ubuntu-24.04 outputs: matrix: ${{ steps.filter.outputs.matrix }} steps: - name: Filter CMT matrix to latest server only id: filter env: CMT_MATRIX: ${{ inputs.CMT_MATRIX }} run: | matrix=$(echo "$CMT_MATRIX" | jq -c '{server: [.server[] | select(.latest == true)]}') if [ "$(echo "$matrix" | jq '.server | length')" -eq 0 ]; then matrix=$(echo "$CMT_MATRIX" | jq -c '{server: [.server[-1]]}') fi echo "matrix=$matrix" >> "$GITHUB_OUTPUT" # PR E2E runs detox provision (demo plugin, license, config) before tests. # CMT previously skipped that and only used Matterwick-booted servers, so # interactive_dialog_plugin failed 24× with "Demo plugin is not active". provision-cmt-servers: name: Provision CMT test servers runs-on: ubuntu-24.04 needs: - calculate-commit-hash steps: - name: Checkout Repository uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: ref: ${{ inputs.MOBILE_VERSION }} persist-credentials: false - name: Collect unique matrix server URLs id: urls env: CMT_MATRIX: ${{ inputs.CMT_MATRIX }} run: | URLS=$(echo "$CMT_MATRIX" | jq -r ' .server[] | .android_site_1_url, .android_site_2_url, .ios_site_1_url, .ios_site_2_url, .site_3_url | select(. != null and . != "") ' | awk 'NF && !seen[$0]++') if [ -z "$URLS" ]; then echo "::error::CMT_MATRIX contained no server URLs to provision" exit 1 fi echo "Provisioning $(echo "$URLS" | wc -l | tr -d ' ') unique server(s)" { echo "list<> "$GITHUB_OUTPUT" - name: Provision test servers uses: ./.github/actions/provision-e2e-servers env: ADMIN_USERNAME: ${{ secrets.MM_MOBILE_E2E_ADMIN_USERNAME }} ADMIN_PASSWORD: ${{ secrets.MM_MOBILE_E2E_ADMIN_PASSWORD }} with: server-urls: ${{ steps.urls.outputs.list }} # Input follows the below schema # { # "server": [ # { # "version": "9.6.1", # "android_site_1_url": "https://android-1.example.test", # "android_site_2_url": "https://android-2.example.test", # "ios_site_1_url": "https://ios-1.example.test", # "ios_site_2_url": "https://ios-2.example.test", # "site_3_url": "https://shared-3.example.test" # }, # { # "version": "9.5.2", # "android_site_1_url": "https://android-1-older.example.test", # "android_site_2_url": "https://android-2-older.example.test", # "ios_site_1_url": "https://ios-1-older.example.test", # "ios_site_2_url": "https://ios-2-older.example.test", # "site_3_url": "https://shared-3-older.example.test" # } # ] # } build-ios-simulator: # macos-14 predates Xcode 26 and has none pre-installed, so prepare-ios-build's # Xcode 26 requirement always fails here. e2e-detox-pr.yml and # e2e-maestro-template.yml already build on macos-26 successfully — match that. runs-on: macos-26 needs: - update-initial-status permissions: contents: read actions: write steps: - name: Checkout Repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false ref: ${{ inputs.MOBILE_VERSION }} - name: Prepare iOS Build uses: ./.github/actions/prepare-ios-build - name: Build iOS Simulator env: TAG: "${{ inputs.MOBILE_VERSION }}" GITHUB_TOKEN: "${{ secrets.MM_MOBILE_GITHUB_TOKEN }}" run: bundle exec fastlane ios simulator --env ios.simulator working-directory: ./fastlane - name: Upload iOS Simulator Build uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: ios-build-simulator-${{ github.run_id }} path: Mattermost-simulator-*.app.zip build-android-apk: runs-on: ubuntu-latest-8-cores needs: - update-initial-status permissions: contents: read actions: write env: ORG_GRADLE_PROJECT_jvmargs: -Xmx8g steps: - name: Prune Docker to free up space run: docker system prune -af - name: Checkout Repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false ref: ${{ inputs.MOBILE_VERSION }} - name: Prepare Android Build uses: ./.github/actions/prepare-android-build env: STORE_FILE: ${{ secrets.MM_MOBILE_STORE_FILE }} STORE_ALIAS: ${{ secrets.MM_MOBILE_STORE_ALIAS }} STORE_PASSWORD: ${{ secrets.MM_MOBILE_STORE_PASSWORD }} MATTERMOST_BUILD_GH_TOKEN: ${{ secrets.MATTERMOST_BUILD_GH_TOKEN }} - name: Detox build run: | cd detox npm ci --prefer-offline --no-audit --no-fund npm run e2e:android-build - name: Upload Android Build uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: android-build-files-${{ github.run_id }} # Match e2e-detox-pr.yml's glob shape exactly: the wildcard must start # right after "build/" so upload-artifact only strips "android/app/build/" # and preserves the outputs/apk/... subtree. e2e-android-template.yml's # Download APK artifact step (shared by PR + CMT) expects that shape. path: "android/app/build/**/*" build-android-apk-maestro: runs-on: ubuntu-latest-8-cores needs: - update-initial-status permissions: contents: read actions: write env: ORG_GRADLE_PROJECT_jvmargs: -Xmx8g steps: - name: Prune Docker to free up space run: docker system prune -af - name: Checkout Repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false ref: ${{ inputs.MOBILE_VERSION }} - name: Prepare Android Build uses: ./.github/actions/prepare-android-build env: STORE_FILE: ${{ secrets.MM_MOBILE_STORE_FILE }} STORE_ALIAS: ${{ secrets.MM_MOBILE_STORE_ALIAS }} STORE_PASSWORD: ${{ secrets.MM_MOBILE_STORE_PASSWORD }} MATTERMOST_BUILD_GH_TOKEN: ${{ secrets.MATTERMOST_BUILD_GH_TOKEN }} - name: Build Maestro Android APK uses: ./.github/actions/build-android-maestro-apk - name: Upload Maestro Android Build uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: android-maestro-build-files-${{ github.run_id }} path: android/app/build/outputs/apk/release/ # latest entry runs full suite, older entries run smoke only. Job-level if: can't conditionally skip matrix legs, so inputs vary instead. detox-e2e: name: mobile-cmt-ios${{ matrix.server.latest && '-full' || '-smoke' }}-${{ matrix.server.version }} permissions: contents: read # nested template mints an OIDC token for the TSIO upload. id-token: write actions: read statuses: write # Nested template finalize reads PR labels to honour E2E/Override; a # reusable workflow cannot escalate past the caller, so grant it here too. issues: read uses: ./.github/workflows/e2e-ios-template.yml needs: - build-ios-simulator - cmt-identity - provision-cmt-servers strategy: fail-fast: false matrix: ${{ fromJson(inputs.CMT_MATRIX) }} secrets: MM_MOBILE_E2E_ADMIN_EMAIL: ${{ secrets.MM_MOBILE_E2E_ADMIN_EMAIL }} MM_MOBILE_E2E_ADMIN_USERNAME: ${{ secrets.MM_MOBILE_E2E_ADMIN_USERNAME }} MM_MOBILE_E2E_ADMIN_PASSWORD: ${{ secrets.MM_MOBILE_E2E_ADMIN_PASSWORD }} MM_MOBILE_E2E_ZEPHYR_API_KEY: ${{ secrets.MM_MOBILE_E2E_ZEPHYR_API_KEY }} MM_MOBILE_E2E_TEST_CYCLE_LINK_PREFIX: ${{ secrets.MM_MOBILE_E2E_TEST_CYCLE_LINK_PREFIX }} MM_MOBILE_E2E_WEBHOOK_URL: ${{ secrets.MM_MOBILE_E2E_WEBHOOK_URL }} MM_E2E_MASTER_HEALTH_WEBHOOK_URL: ${{ secrets.MM_E2E_MASTER_HEALTH_WEBHOOK_URL }} MM_E2E_RELEASE_WEBHOOK_URL: ${{ secrets.MM_E2E_RELEASE_WEBHOOK_URL }} MM_MOBILE_DETOX_AWS_ACCESS_KEY_ID: ${{ secrets.MM_MOBILE_DETOX_AWS_ACCESS_KEY_ID }} MM_MOBILE_DETOX_AWS_SECRET_ACCESS_KEY: ${{ secrets.MM_MOBILE_DETOX_AWS_SECRET_ACCESS_KEY }} MM_MOBILE_E2E_WEBHOOK_PUBLIC_BASE_URL: ${{ secrets.MM_MOBILE_E2E_WEBHOOK_PUBLIC_BASE_URL }} MM_MOBILE_E2E_CLOUDFLARED_TUNNEL_TOKEN: ${{ secrets.MM_MOBILE_E2E_CLOUDFLARED_TUNNEL_TOKEN }} with: tsio-config: ${{ needs.cmt-identity.outputs.tsio-base-config }} tsio-shard-name: detox-ios-Server_${{ matrix.server.version }} run-type: "RELEASE" MM_TEST_SERVER_URL: ${{ matrix.server.ios_site_1_url }} MM_TEST_SERVER_URL_2: ${{ matrix.server.ios_site_2_url }} MM_TEST_SERVER_URL_3: ${{ matrix.server.site_3_url }} MOBILE_VERSION: ${{ inputs.MOBILE_VERSION }} search_path: ${{ matrix.server.latest && 'detox/e2e/test' || 'detox/e2e/test/products/channels/smoke_test' }} parallelism: ${{ matrix.server.latest && '10' || '1' }} artifact_run_id: ${{ github.run_id }} detox-ipad-e2e: name: mobile-cmt-ipad-${{ matrix.server.version }} permissions: contents: read id-token: write actions: read statuses: write # Nested template finalize reads PR labels to honour E2E/Override; a # reusable workflow cannot escalate past the caller, so grant it here too. issues: read uses: ./.github/workflows/e2e-ios-template.yml needs: - build-ios-simulator - cmt-identity - provision-cmt-servers strategy: fail-fast: false matrix: ${{ fromJson(inputs.CMT_MATRIX) }} secrets: MM_MOBILE_E2E_ADMIN_EMAIL: ${{ secrets.MM_MOBILE_E2E_ADMIN_EMAIL }} MM_MOBILE_E2E_ADMIN_USERNAME: ${{ secrets.MM_MOBILE_E2E_ADMIN_USERNAME }} MM_MOBILE_E2E_ADMIN_PASSWORD: ${{ secrets.MM_MOBILE_E2E_ADMIN_PASSWORD }} MM_MOBILE_E2E_ZEPHYR_API_KEY: ${{ secrets.MM_MOBILE_E2E_ZEPHYR_API_KEY }} MM_MOBILE_E2E_TEST_CYCLE_LINK_PREFIX: ${{ secrets.MM_MOBILE_E2E_TEST_CYCLE_LINK_PREFIX }} MM_MOBILE_E2E_WEBHOOK_URL: ${{ secrets.MM_MOBILE_E2E_WEBHOOK_URL }} MM_E2E_MASTER_HEALTH_WEBHOOK_URL: ${{ secrets.MM_E2E_MASTER_HEALTH_WEBHOOK_URL }} MM_E2E_RELEASE_WEBHOOK_URL: ${{ secrets.MM_E2E_RELEASE_WEBHOOK_URL }} MM_MOBILE_DETOX_AWS_ACCESS_KEY_ID: ${{ secrets.MM_MOBILE_DETOX_AWS_ACCESS_KEY_ID }} MM_MOBILE_DETOX_AWS_SECRET_ACCESS_KEY: ${{ secrets.MM_MOBILE_DETOX_AWS_SECRET_ACCESS_KEY }} MM_MOBILE_E2E_WEBHOOK_PUBLIC_BASE_URL: ${{ secrets.MM_MOBILE_E2E_WEBHOOK_PUBLIC_BASE_URL }} MM_MOBILE_E2E_CLOUDFLARED_TUNNEL_TOKEN: ${{ secrets.MM_MOBILE_E2E_CLOUDFLARED_TUNNEL_TOKEN }} with: tsio-config: ${{ needs.cmt-identity.outputs.tsio-base-config }} tsio-shard-name: detox-ipad-Server_${{ matrix.server.version }} run-type: "RELEASE" MM_TEST_SERVER_URL: ${{ matrix.server.ios_site_1_url }} MM_TEST_SERVER_URL_2: ${{ matrix.server.ios_site_2_url }} MM_TEST_SERVER_URL_3: ${{ matrix.server.site_3_url }} MOBILE_VERSION: ${{ inputs.MOBILE_VERSION }} ios_device_name: "iPad Pro 13-inch (M5)" ios_device_os_name: "iOS 26.2" search_path: "detox/e2e/test/products/channels/ipad" parallelism: "1" detox_config: "ios.ipad.debug" artifact_run_id: ${{ github.run_id }} # Maestro runs only against the latest server version (no smoke-on-older variant). maestro-ios-e2e: name: mobile-cmt-maestro-ios-${{ matrix.server.version }} permissions: contents: read id-token: write actions: read statuses: write # Nested template finalize reads PR labels to honour E2E/Override; a # reusable workflow cannot escalate past the caller, so grant it here too. issues: read uses: ./.github/workflows/e2e-maestro-template.yml needs: - build-ios-simulator - prepare-maestro-matrix - cmt-identity - provision-cmt-servers strategy: fail-fast: false matrix: ${{ fromJson(needs.prepare-maestro-matrix.outputs.matrix) }} secrets: MM_MOBILE_E2E_ADMIN_EMAIL: ${{ secrets.MM_MOBILE_E2E_ADMIN_EMAIL }} MM_MOBILE_E2E_ADMIN_USERNAME: ${{ secrets.MM_MOBILE_E2E_ADMIN_USERNAME }} MM_MOBILE_E2E_ADMIN_PASSWORD: ${{ secrets.MM_MOBILE_E2E_ADMIN_PASSWORD }} MM_MOBILE_E2E_ZEPHYR_API_KEY: ${{ secrets.MM_MOBILE_E2E_ZEPHYR_API_KEY }} MM_MOBILE_DETOX_AWS_ACCESS_KEY_ID: ${{ secrets.MM_MOBILE_DETOX_AWS_ACCESS_KEY_ID }} MM_MOBILE_DETOX_AWS_SECRET_ACCESS_KEY: ${{ secrets.MM_MOBILE_DETOX_AWS_SECRET_ACCESS_KEY }} MM_MOBILE_E2E_WEBHOOK_URL: ${{ secrets.MM_MOBILE_E2E_WEBHOOK_URL }} MM_E2E_MASTER_HEALTH_WEBHOOK_URL: ${{ secrets.MM_E2E_MASTER_HEALTH_WEBHOOK_URL }} MM_E2E_RELEASE_WEBHOOK_URL: ${{ secrets.MM_E2E_RELEASE_WEBHOOK_URL }} with: tsio-config: ${{ needs.cmt-identity.outputs.tsio-base-config }} tsio-shard-name: maestro-ios-Server_${{ matrix.server.version }} platform: ios run-type: "RELEASE" MM_TEST_SERVER_URL: ${{ matrix.server.ios_site_1_url }} MOBILE_VERSION: ${{ inputs.MOBILE_VERSION }} record_tests_in_zephyr: true artifact_run_id: ${{ github.run_id }} detox-android-e2e: name: mobile-cmt-android${{ matrix.server.latest && '-full' || '-smoke' }}-${{ matrix.server.version }} permissions: contents: read id-token: write actions: read statuses: write # Nested template finalize reads PR labels to honour E2E/Override; a # reusable workflow cannot escalate past the caller, so grant it here too. issues: read uses: ./.github/workflows/e2e-android-template.yml needs: - build-android-apk - cmt-identity - provision-cmt-servers strategy: fail-fast: false matrix: ${{ fromJson(inputs.CMT_MATRIX) }} secrets: MM_MOBILE_E2E_ADMIN_EMAIL: ${{ secrets.MM_MOBILE_E2E_ADMIN_EMAIL }} MM_MOBILE_E2E_ADMIN_USERNAME: ${{ secrets.MM_MOBILE_E2E_ADMIN_USERNAME }} MM_MOBILE_E2E_ADMIN_PASSWORD: ${{ secrets.MM_MOBILE_E2E_ADMIN_PASSWORD }} MM_MOBILE_E2E_ZEPHYR_API_KEY: ${{ secrets.MM_MOBILE_E2E_ZEPHYR_API_KEY }} MM_MOBILE_E2E_TEST_CYCLE_LINK_PREFIX: ${{ secrets.MM_MOBILE_E2E_TEST_CYCLE_LINK_PREFIX }} MM_MOBILE_E2E_WEBHOOK_URL: ${{ secrets.MM_MOBILE_E2E_WEBHOOK_URL }} MM_E2E_MASTER_HEALTH_WEBHOOK_URL: ${{ secrets.MM_E2E_MASTER_HEALTH_WEBHOOK_URL }} MM_E2E_RELEASE_WEBHOOK_URL: ${{ secrets.MM_E2E_RELEASE_WEBHOOK_URL }} MM_MOBILE_DETOX_AWS_ACCESS_KEY_ID: ${{ secrets.MM_MOBILE_DETOX_AWS_ACCESS_KEY_ID }} MM_MOBILE_DETOX_AWS_SECRET_ACCESS_KEY: ${{ secrets.MM_MOBILE_DETOX_AWS_SECRET_ACCESS_KEY }} MM_MOBILE_E2E_WEBHOOK_PUBLIC_BASE_URL: ${{ secrets.MM_MOBILE_E2E_WEBHOOK_PUBLIC_BASE_URL }} MM_MOBILE_E2E_CLOUDFLARED_TUNNEL_TOKEN: ${{ secrets.MM_MOBILE_E2E_CLOUDFLARED_TUNNEL_TOKEN }} with: tsio-config: ${{ needs.cmt-identity.outputs.tsio-base-config }} tsio-shard-name: detox-android-Server_${{ matrix.server.version }} run-android-tests: true run-type: "RELEASE" MM_TEST_SERVER_URL: ${{ matrix.server.android_site_1_url }} MM_TEST_SERVER_URL_2: ${{ matrix.server.android_site_2_url }} MM_TEST_SERVER_URL_3: ${{ matrix.server.site_3_url }} MOBILE_VERSION: ${{ inputs.MOBILE_VERSION }} search_path: ${{ matrix.server.latest && 'detox/e2e/test' || 'detox/e2e/test/products/channels/smoke_test' }} parallelism: ${{ matrix.server.latest && '10' || '1' }} artifact_run_id: ${{ github.run_id }} # Maestro runs only against the latest server version (no smoke-on-older variant). maestro-android-e2e: name: mobile-cmt-maestro-android-${{ matrix.server.version }} permissions: contents: read id-token: write actions: read statuses: write # Nested template finalize reads PR labels to honour E2E/Override; a # reusable workflow cannot escalate past the caller, so grant it here too. issues: read uses: ./.github/workflows/e2e-maestro-template.yml needs: - build-android-apk-maestro - prepare-maestro-matrix - cmt-identity - provision-cmt-servers strategy: fail-fast: false matrix: ${{ fromJson(needs.prepare-maestro-matrix.outputs.matrix) }} secrets: MM_MOBILE_E2E_ADMIN_EMAIL: ${{ secrets.MM_MOBILE_E2E_ADMIN_EMAIL }} MM_MOBILE_E2E_ADMIN_USERNAME: ${{ secrets.MM_MOBILE_E2E_ADMIN_USERNAME }} MM_MOBILE_E2E_ADMIN_PASSWORD: ${{ secrets.MM_MOBILE_E2E_ADMIN_PASSWORD }} MM_MOBILE_E2E_ZEPHYR_API_KEY: ${{ secrets.MM_MOBILE_E2E_ZEPHYR_API_KEY }} MM_MOBILE_DETOX_AWS_ACCESS_KEY_ID: ${{ secrets.MM_MOBILE_DETOX_AWS_ACCESS_KEY_ID }} MM_MOBILE_DETOX_AWS_SECRET_ACCESS_KEY: ${{ secrets.MM_MOBILE_DETOX_AWS_SECRET_ACCESS_KEY }} MM_MOBILE_E2E_WEBHOOK_URL: ${{ secrets.MM_MOBILE_E2E_WEBHOOK_URL }} MM_E2E_MASTER_HEALTH_WEBHOOK_URL: ${{ secrets.MM_E2E_MASTER_HEALTH_WEBHOOK_URL }} MM_E2E_RELEASE_WEBHOOK_URL: ${{ secrets.MM_E2E_RELEASE_WEBHOOK_URL }} with: tsio-config: ${{ needs.cmt-identity.outputs.tsio-base-config }} tsio-shard-name: maestro-android-Server_${{ matrix.server.version }} platform: android run-type: "RELEASE" MM_TEST_SERVER_URL: ${{ matrix.server.android_site_1_url }} MOBILE_VERSION: ${{ inputs.MOBILE_VERSION }} artifact_run_id: ${{ github.run_id }} record_tests_in_zephyr: true update-final-status: runs-on: ubuntu-24.04 if: always() permissions: contents: read statuses: write # the channel rollup mints an OIDC token to read each shard's TSIO group. id-token: write needs: - cmt-identity - calculate-commit-hash - provision-cmt-servers - detox-e2e - detox-android-e2e - detox-ipad-e2e - maestro-ios-e2e - maestro-android-e2e steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: ref: ${{ inputs.MOBILE_VERSION }} persist-credentials: false - name: Resolve upstream matrix outcome id: upstream run: | UPSTREAM="true" for r in "${{ needs.provision-cmt-servers.result }}" "${{ needs.detox-e2e.result }}" "${{ needs.detox-android-e2e.result }}" "${{ needs.detox-ipad-e2e.result }}" "${{ needs.maestro-ios-e2e.result }}" "${{ needs.maestro-android-e2e.result }}"; do case "$r" in success|skipped) ;; *) UPSTREAM="false" ;; esac done echo "succeeded=$UPSTREAM" >> "$GITHUB_OUTPUT" # Umbrella context only — each matrix leg already posted from TSIO. - name: Finalize umbrella CMT commit status from CI job results env: GITHUB_TOKEN: ${{ github.token }} COMMIT_SHA: ${{ needs.calculate-commit-hash.outputs.MOBILE_SHA }} REPO: ${{ github.repository }} TARGET_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} UPSTREAM: ${{ steps.upstream.outputs.succeeded }} run: | if [ "$UPSTREAM" = "true" ]; then STATE=success DESC="Compatibility Matrix Testing completed — see per-job e2e-test/* checks" else STATE=failure DESC="Compatibility Matrix Testing failed — see per-job e2e-test/* checks" fi gh api --method POST "repos/${REPO}/statuses/${COMMIT_SHA}" \ -f state="$STATE" \ -f context="e2e-test/compatibility-matrix-testing" \ -f description="$DESC" \ -f target_url="$TARGET_URL" # Per-shard TSIO groups are finalized in each matrix leg, which leaves channel # notify off so N legs do not spam. This umbrella polls every shard group and # posts one aggregated summary (no second commit status) — same path as PR/main. - name: Post Mattermost CMT channel rollup if: always() env: JOB_CONFIGS: ${{ needs.cmt-identity.outputs.tsio-job-configs }} MATTERMOST_CMT_WEBHOOK_URL: ${{ secrets.MM_E2E_RELEASE_WEBHOOK_URL }} UPSTREAM_SUCCEEDED: ${{ steps.upstream.outputs.succeeded }} run: | node detox/utils/tsio-channel-notify-rollup.js \ --job-configs="$JOB_CONFIGS" \ --upstream-succeeded="$UPSTREAM_SUCCEEDED" \ --poll-attempts=6 || true # Instance cleanup is handled by matterwick on workflow_run completed.