mirror of
https://github.com/mattermost/mattermost-mobile.git
synced 2026-11-15 07:30:30 +00:00
* ci(e2e): per-job TSIO report groups and commit statuses E2E jobs previously shared a single `e2e/mobile` commit status backed by one TSIO report group, so concurrent platform legs raced to finalize the group and clobbered each other's results. Give each Detox/Maestro job its own report group (`mobile-<flow>-<job>`, total_reports_expected=1) and its own commit status context (`detox-ios`, `detox-android`, `detox-ipad`, `maestro-ios`, `maestro-android`); CMT reports per shard plus an umbrella context. Channel notifications move to a single rollup step so N platform legs no longer spam the webhook, and the five-server topology is dispatched with explicit per-platform inputs that still resolve to the existing SITE_*_URL contract consumed by the test config. Also adds the webhook sidecar launcher, Android emulator CI prep, and TSIO screenshot path linking that keeps folder basenames matching Jest full names. The new detox type-check job surfaced pre-existing broken import paths in maestro/fixtures/calls_seed.ts, which pointed at detox/detox/e2e; corrected so the guard passes on its own first run. Note: the CMT matrix now reads per-platform server URLs (`ios_site_1_url`, `android_site_1_url`, `site_3_url`) instead of a single `url`, which needs the matching Matterwick dispatch change to land alongside this. Co-authored-by: Cursor <cursoragent@cursor.com> * ci(e2e): aggregate CMT channel rollup from TSIO and wire notify secrets The CMT umbrella posted a placeholder summary built from CI job outcomes (passed:0, failed:0|1, reports:[]), so the release channel message carried no per-leg detail. It now runs the same tsio-channel-notify-rollup.js path as PR/main: cmt-identity emits a per-shard TSIO config map and the rollup polls every shard group, so counts and per-leg rows come from TSIO. The rollup job also needs id-token: write to mint its OIDC token. Webhook secrets used by the per-job TSIO finalize steps were referenced but never declared in the templates' workflow_call.secrets, so GitHub resolved them to empty strings and channel notify silently no-opped. Declare them in the iOS/Android/Maestro templates and forward them from e2e-detox.yml and the CMT callers. The Maestro PR path worked only because its callers use secrets: inherit. Also from review: - split-tests.js emitted two matrix jobs at parallelism=1 when mm_blocks specs were present; merge into one shard instead of exceeding the requested count. - Move the Android ARTIFACT_RUN_ID emptiness check ahead of download-artifact, which consumes it and otherwise fails with an opaque API error. - resolve-tsio-job-config.sh now reuses webhookBucketForReportName instead of re-deriving the bucket prefix with a looser startsWith check. Co-authored-by: Cursor <cursoragent@cursor.com> * ci(e2e): add local preflight for CI PRs The webhook-secret and OIDC-permission gaps in this branch were invisible locally and in a green CI run — the affected steps exit 0 and just skip the notify or commit status. Add a script engineers can run before opening a PR that touches .github/ or detox/utils: cd detox && npm run preflight:ci # :ci-fast skips lint + tsc It runs actionlint (which flags secrets not declared in a callee's workflow_call.secrets), scans for exit-code capture broken by a trailing backslash, asserts every job running a TSIO OIDC script declares id-token: write, then runs the detox/utils tests and npm run check. actionlint and the CodeRabbit CLI are optional and skipped with an install hint when absent. Co-authored-by: Cursor <cursoragent@cursor.com> * ci(e2e): use mktemp for preflight logs Fixed /tmp log paths can be pre-created as symlinks, so the preflight script would truncate whatever they point at (CWE-377). Create both logs with mktemp and drop them on exit. Co-authored-by: Cursor <cursoragent@cursor.com> * ci(e2e): address review feedback on TSIO statuses and sidecar A cancelled E2E run reached no conclusion, so posting `success` on its commit statuses let a PR merge on a check that never ran. Cancelled runs now post `pending`; `success` is reserved for the one deliberate bypass, the E2E/Override label. Commit-status contexts move into the `e2e-test/*` namespace to match the mattermost monorepo, with the prefix owned by build-tsio-job-config.js and mirrored in the cancel action's context list. Legacy unprefixed and `e2e/*` contexts are dropped rather than carried forward. The `preflight:ci` script and its OIDC-permission checker duplicated what actionlint, the unit tests and `npm run check` already do, so they are removed and detox/CLAUDE.md documents the underlying failure modes and the commands to catch them instead. cloudflared and the TSIO CLI are pinned to known-good releases rather than `latest`, and detox/README.md now documents the webhook sidecar: when a tunnel is needed, the WEBHOOK_PUBLIC_BASE_URL / CLOUDFLARED_TUNNEL_TOKEN precedence, and why quick tunnels report callbacks as unreachable. Co-authored-by: Cursor <cursoragent@cursor.com> * ci(e2e): detect upstream cancellation and surface status reset failures generate-report runs with always(), so job.status stays "success" when an upstream job is cancelled and TSIO was published as a real failure. Read the generate-specs/e2e-* needs results so a cancelled run is reported as such. In cancel-e2e-runs, only reset the E2E commit statuses for runs that were actually stopped — a run we failed to cancel keeps publishing its own results. Aggregate createCommitStatus failures across every context and fail the action afterwards instead of silently warning, so a stale check cannot go unnoticed. --------- Co-authored-by: Cursor <cursoragent@cursor.com>
548 lines
23 KiB
YAML
548 lines
23 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
|
|
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|