mattermost-mobile-upstream/.github/workflows/compatibility-matrix-testing.yml
yasser khan 587e15ea89
chore(e2e): Cache app builds. Skip E2E on docs only PR. (#9930)
Based on the later commits (after the earlier experimentation and reversions), the meaningful end state of the PR is:

**Summary:** Improve E2E CI reliability, performance, and security by introducing robust build caching with safe fingerprinting and cache isolation, duration-based test sharding with graceful fallbacks, more reliable E2E impact detection and status handling, stronger cancellation/concurrency behavior, multiple iOS Detox stability fixes, and hardening GitHub Actions to avoid executing PR-authored code. Also includes CI fixes for iOS builds and a temporary Android E2E test skip for a known regression.

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
2026-08-04 19:13:39 +05:30

568 lines
25 KiB
YAML

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"
# 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
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
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
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
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
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
- 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.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 <shard> 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.