From 62b0e27ae26f2ebae1e9fa4423907488bf889bf6 Mon Sep 17 00:00:00 2001 From: yasserfaraazkhan Date: Tue, 30 Jun 2026 09:13:28 +0530 Subject: [PATCH 1/5] fix(e2e): fix applesimutils brew trust ordering and improve CI failure messages - brew trust wix/brew was running before brew tap wix/brew, causing "Refusing to load formula from untrusted tap" errors on fresh runners. Fixed by tapping before trusting, and removing || true suppression. - When all test machines fail before producing results, the PR check showed "? passed, ? failed" because the report-save step was blocking the failure-counting step. Fixed with continue-on-error: true. - When no S3 report exists (infra failure), clicking the check now falls back to the GitHub Actions run URL instead of going nowhere. Fixes the E2E failure on #9887. --- .github/workflows/e2e-detox.yml | 6 +++--- .github/workflows/e2e-ios-template.yml | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/e2e-detox.yml b/.github/workflows/e2e-detox.yml index 9f6a16567b7..547c5eba906 100644 --- a/.github/workflows/e2e-detox.yml +++ b/.github/workflows/e2e-detox.yml @@ -134,7 +134,7 @@ jobs: || needs.run-ios-tests.outputs.FAILURES == '0' && format('All {0} tests passed', needs.run-ios-tests.outputs.TOTAL) || format('{0} passed, {1} failed, {2} skipped', needs.run-ios-tests.outputs.PASSES || '?', needs.run-ios-tests.outputs.FAILURES || '?', needs.run-ios-tests.outputs.SKIPPED || '0') }} status: ${{ (needs.run-ios-tests.result == 'skipped' && 'failure') || needs.run-ios-tests.outputs.STATUS || 'failure' }} - target_url: ${{ needs.run-ios-tests.outputs.TARGET_URL }} + target_url: ${{ needs.run-ios-tests.outputs.TARGET_URL || format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }} update-final-status-android: runs-on: ubuntu-22.04 @@ -162,7 +162,7 @@ jobs: || needs.run-android-tests.outputs.FAILURES == '0' && format('All {0} tests passed', needs.run-android-tests.outputs.TOTAL) || format('{0} passed, {1} failed, {2} skipped', needs.run-android-tests.outputs.PASSES || '?', needs.run-android-tests.outputs.FAILURES || '?', needs.run-android-tests.outputs.SKIPPED || '0') }} status: ${{ (needs.run-android-tests.result == 'skipped' && 'failure') || needs.run-android-tests.outputs.STATUS || 'failure' }} - target_url: ${{ needs.run-android-tests.outputs.TARGET_URL }} + target_url: ${{ needs.run-android-tests.outputs.TARGET_URL || format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }} update-final-status-ipad: runs-on: ubuntu-22.04 @@ -190,4 +190,4 @@ jobs: || needs.run-ios-ipad-tests.outputs.FAILURES == '0' && format('All {0} tests passed', needs.run-ios-ipad-tests.outputs.TOTAL) || format('{0} passed, {1} failed, {2} skipped', needs.run-ios-ipad-tests.outputs.PASSES || '?', needs.run-ios-ipad-tests.outputs.FAILURES || '?', needs.run-ios-ipad-tests.outputs.SKIPPED || '0') }} status: ${{ (needs.run-ios-ipad-tests.result == 'skipped' && 'failure') || needs.run-ios-ipad-tests.outputs.STATUS || 'failure' }} - target_url: ${{ needs.run-ios-ipad-tests.outputs.TARGET_URL }} + target_url: ${{ needs.run-ios-ipad-tests.outputs.TARGET_URL || format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }} diff --git a/.github/workflows/e2e-ios-template.yml b/.github/workflows/e2e-ios-template.yml index 0ae20744259..683f107d77c 100644 --- a/.github/workflows/e2e-ios-template.yml +++ b/.github/workflows/e2e-ios-template.yml @@ -212,9 +212,10 @@ jobs: - name: Install Homebrew Dependencies run: | - brew trust wix/brew 2>/dev/null || true + # Trust must come after tap — the tap directory must exist before brew trust can act on it. brew tap wix/brew - brew install applesimutils || true + brew trust wix/brew + brew install applesimutils # Cache restores Cellar without /opt/homebrew/bin symlinks — link the tap formula explicitly. brew link --overwrite --force wix/brew/applesimutils 2>/dev/null \ || brew link --overwrite applesimutils @@ -717,6 +718,7 @@ jobs: - name: Save report Detox Dependencies id: report-link + continue-on-error: true run: | cd detox npm ci From 0edd369e5940fcc8445d637c7014d139494f3668 Mon Sep 17 00:00:00 2001 From: yasserfaraazkhan Date: Tue, 30 Jun 2026 09:27:05 +0530 Subject: [PATCH 2/5] align maestro reporting --- .github/workflows/e2e-maestro-pr.yml | 4 ++-- .github/workflows/e2e-maestro-template.yml | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/e2e-maestro-pr.yml b/.github/workflows/e2e-maestro-pr.yml index 4f3a160136e..cbb8692a859 100644 --- a/.github/workflows/e2e-maestro-pr.yml +++ b/.github/workflows/e2e-maestro-pr.yml @@ -133,7 +133,7 @@ jobs: env: COMMIT_SHA: ${{ inputs.MOBILE_VERSION }} STATUS: ${{ (needs.run-maestro-ios.result == 'skipped' && 'failure') || needs.run-maestro-ios.outputs.STATUS || 'failure' }} - TARGET_URL: ${{ needs.run-maestro-ios.outputs.TARGET_URL }} + TARGET_URL: ${{ needs.run-maestro-ios.outputs.TARGET_URL || format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }} DESCRIPTION: >- ${{ needs.run-maestro-ios.result == 'skipped' && 'Build failed — Maestro iOS tests did not run' || needs.run-maestro-ios.outputs.FAILURES == '-1' && 'All test machines failed before producing results' @@ -167,7 +167,7 @@ jobs: env: COMMIT_SHA: ${{ inputs.MOBILE_VERSION }} STATUS: ${{ (needs.run-maestro-android.result == 'skipped' && 'failure') || needs.run-maestro-android.outputs.STATUS || 'failure' }} - TARGET_URL: ${{ needs.run-maestro-android.outputs.TARGET_URL }} + TARGET_URL: ${{ needs.run-maestro-android.outputs.TARGET_URL || format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }} DESCRIPTION: >- ${{ needs.run-maestro-android.result == 'skipped' && 'Build failed — Maestro Android tests did not run' || needs.run-maestro-android.outputs.FAILURES == '-1' && 'All test machines failed before producing results' diff --git a/.github/workflows/e2e-maestro-template.yml b/.github/workflows/e2e-maestro-template.yml index 4dc5c558a30..df4dad26ca9 100644 --- a/.github/workflows/e2e-maestro-template.yml +++ b/.github/workflows/e2e-maestro-template.yml @@ -360,10 +360,10 @@ jobs: commitSha: process.env.GITHUB_SHA || '', }); } else { - console.log('No Maestro report found — marking as failure'); - ['FAILURES=1','PASSES=0','TOTAL=0','ERRORS=1','SKIPPED=0','PERCENTAGE=0','NO_REPORT=true'].forEach(l => fs.appendFileSync(process.env.GITHUB_OUTPUT, l + '\n')); + console.log('No Maestro report found — marking as infrastructure failure'); + ['FAILURES=-1','PASSES=0','TOTAL=0','ERRORS=0','SKIPPED=0','PERCENTAGE=0','NO_REPORT=true'].forEach(l => fs.appendFileSync(process.env.GITHUB_OUTPUT, l + '\n')); } - " + " || grep -q '^FAILURES=' "$GITHUB_OUTPUT" 2>/dev/null || printf 'FAILURES=-1\nPASSES=0\nTOTAL=0\nERRORS=0\nSKIPPED=0\nPERCENTAGE=0\n' >> "$GITHUB_OUTPUT" - name: Upload Maestro Report to S3 if: always() @@ -698,10 +698,10 @@ jobs: commitSha: process.env.GITHUB_SHA || '', }); } else { - console.log('No Maestro report found — marking as failure'); - ['FAILURES=1','PASSES=0','TOTAL=0','ERRORS=1','SKIPPED=0','PERCENTAGE=0','NO_REPORT=true'].forEach(l => fs.appendFileSync(process.env.GITHUB_OUTPUT, l + '\n')); + console.log('No Maestro report found — marking as infrastructure failure'); + ['FAILURES=-1','PASSES=0','TOTAL=0','ERRORS=0','SKIPPED=0','PERCENTAGE=0','NO_REPORT=true'].forEach(l => fs.appendFileSync(process.env.GITHUB_OUTPUT, l + '\n')); } - " + " || grep -q '^FAILURES=' "$GITHUB_OUTPUT" 2>/dev/null || printf 'FAILURES=-1\nPASSES=0\nTOTAL=0\nERRORS=0\nSKIPPED=0\nPERCENTAGE=0\n' >> "$GITHUB_OUTPUT" - name: Upload Maestro Report to S3 if: always() From f81756f01e6f884512d56f3a10c1b3d35b801fac Mon Sep 17 00:00:00 2001 From: yasserfaraazkhan Date: Tue, 30 Jun 2026 09:44:20 +0530 Subject: [PATCH 3/5] fix comments --- .github/actions/cancel-e2e-runs/action.yml | 2 +- .github/workflows/e2e-ios-template.yml | 1 + .github/workflows/e2e-maestro-template.yml | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/actions/cancel-e2e-runs/action.yml b/.github/actions/cancel-e2e-runs/action.yml index 55c5c61e59a..006b14aaf75 100644 --- a/.github/actions/cancel-e2e-runs/action.yml +++ b/.github/actions/cancel-e2e-runs/action.yml @@ -101,7 +101,7 @@ runs: owner, repo, sha, - state: 'error', + state: 'success', context: statusContext, description: reason.substring(0, 140), }); diff --git a/.github/workflows/e2e-ios-template.yml b/.github/workflows/e2e-ios-template.yml index 683f107d77c..964d5a6e822 100644 --- a/.github/workflows/e2e-ios-template.yml +++ b/.github/workflows/e2e-ios-template.yml @@ -754,6 +754,7 @@ jobs: - name: Set Target URL id: set-url + if: steps.report-link.outcome == 'success' run: | echo "TARGET_URL=https://${{ env.DETOX_AWS_S3_BUCKET }}.s3.amazonaws.com/${{ steps.s3.outputs.path }}/jest-stare/ios-report.html" >> ${GITHUB_OUTPUT} diff --git a/.github/workflows/e2e-maestro-template.yml b/.github/workflows/e2e-maestro-template.yml index df4dad26ca9..c53b61e89a0 100644 --- a/.github/workflows/e2e-maestro-template.yml +++ b/.github/workflows/e2e-maestro-template.yml @@ -385,7 +385,7 @@ jobs: - name: Set Target URL id: set-url - if: always() + if: always() && steps.summary.conclusion == 'success' && steps.summary.outputs.FAILURES != '-1' env: S3_BUCKET: ${{ env.DETOX_AWS_S3_BUCKET }} run: | @@ -723,7 +723,7 @@ jobs: - name: Set Target URL id: set-url - if: always() + if: always() && steps.summary.conclusion == 'success' && steps.summary.outputs.FAILURES != '-1' env: S3_BUCKET: ${{ env.DETOX_AWS_S3_BUCKET }} run: | From 4a4751580be5c992065b6e97929e4495baf1d6a7 Mon Sep 17 00:00:00 2001 From: yasserfaraazkhan Date: Tue, 30 Jun 2026 10:07:54 +0530 Subject: [PATCH 4/5] mark e2e jobs skipped if the label is removed and they get cancelled --- .github/actions/cancel-e2e-runs/action.yml | 16 ++- .../actions/update-commit-status/action.yml | 50 ++++++-- .../workflows/e2e-cancel-on-label-removal.yml | 2 +- .github/workflows/e2e-detox-pr.yml | 16 +-- .github/workflows/e2e-detox.yml | 12 +- .github/workflows/e2e-label-manager.yml | 4 +- .github/workflows/e2e-maestro-pr.yml | 112 ++++++++---------- 7 files changed, 117 insertions(+), 95 deletions(-) diff --git a/.github/actions/cancel-e2e-runs/action.yml b/.github/actions/cancel-e2e-runs/action.yml index 006b14aaf75..f2329db0667 100644 --- a/.github/actions/cancel-e2e-runs/action.yml +++ b/.github/actions/cancel-e2e-runs/action.yml @@ -97,16 +97,20 @@ runs: const markCommitStatusesCancelled = async (sha) => { for (const statusContext of E2E_STATUS_CONTEXTS) { try { - await github.rest.repos.createCommitStatus({ + await github.rest.checks.create({ owner, repo, - sha, - state: 'success', - context: statusContext, - description: reason.substring(0, 140), + name: statusContext, + head_sha: sha, + status: 'completed', + conclusion: 'skipped', + output: { + title: reason.substring(0, 140), + summary: reason.substring(0, 140), + }, }); } catch (err) { - core.warning(`createCommitStatus ${statusContext} on ${sha}: ${err.message}`); + core.warning(`checks.create ${statusContext} on ${sha}: ${err.message}`); } } }; diff --git a/.github/actions/update-commit-status/action.yml b/.github/actions/update-commit-status/action.yml index 777e207cd41..390879fb780 100644 --- a/.github/actions/update-commit-status/action.yml +++ b/.github/actions/update-commit-status/action.yml @@ -1,5 +1,5 @@ name: update-commit-status -description: Set a GitHub commit status (Node 24 / github-script v9) +description: Set a GitHub check run status (Node 24 / github-script v9) inputs: repository_full_name: @@ -12,7 +12,7 @@ inputs: description: required: true status: - description: success | failure | pending | error + description: success | failure | pending | error | skipped required: true target_url: required: false @@ -33,12 +33,46 @@ runs: script: | const [owner, repo] = process.env.REPOSITORY_FULL_NAME.split('/'); const targetUrl = process.env.TARGET_URL?.trim(); - await github.rest.repos.createCommitStatus({ + const status = process.env.STATUS; + const description = (process.env.DESCRIPTION || '').substring(0, 140); + + const validStatuses = new Set(['success', 'failure', 'pending', 'error', 'skipped']); + if (!validStatuses.has(status)) { + core.setFailed(`Unsupported status "${status}". Expected one of: ${[...validStatuses].join(', ')}`); + return; + } + + const isCompleted = status !== 'pending'; + const conclusionMap = { success: 'success', failure: 'failure', error: 'failure', skipped: 'skipped' }; + + const payload = { + status: isCompleted ? 'completed' : 'in_progress', + ...(isCompleted ? { conclusion: conclusionMap[status] } : {}), + output: { title: description, summary: description }, + ...(targetUrl ? { details_url: targetUrl } : {}), + }; + + if (isCompleted) { + // Update the existing in_progress run rather than creating a second orphaned one. + const { data: { check_runs } } = await github.rest.checks.listForRef({ + owner, + repo, + ref: process.env.COMMIT_SHA, + check_name: process.env.CONTEXT, + filter: 'latest', + per_page: 1, + }); + const existing = check_runs.find(r => r.status !== 'completed'); + if (existing) { + await github.rest.checks.update({ owner, repo, check_run_id: existing.id, ...payload }); + return; + } + } + + await github.rest.checks.create({ owner, repo, - sha: process.env.COMMIT_SHA, - state: process.env.STATUS, - context: process.env.CONTEXT, - description: (process.env.DESCRIPTION || '').substring(0, 140), - ...(targetUrl ? {target_url: targetUrl} : {}), + name: process.env.CONTEXT, + head_sha: process.env.COMMIT_SHA, + ...payload, }); diff --git a/.github/workflows/e2e-cancel-on-label-removal.yml b/.github/workflows/e2e-cancel-on-label-removal.yml index 3caa1a80d8a..5a0be7e8ad3 100644 --- a/.github/workflows/e2e-cancel-on-label-removal.yml +++ b/.github/workflows/e2e-cancel-on-label-removal.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-22.04 permissions: actions: write - statuses: write + checks: write steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Cancel in-progress E2E workflow runs diff --git a/.github/workflows/e2e-detox-pr.yml b/.github/workflows/e2e-detox-pr.yml index 9387daa1468..3d2735d62a9 100644 --- a/.github/workflows/e2e-detox-pr.yml +++ b/.github/workflows/e2e-detox-pr.yml @@ -68,10 +68,10 @@ jobs: runs-on: ubuntu-22.04 permissions: actions: write + checks: write contents: read pull-requests: read issues: read - statuses: write outputs: e2e_override: ${{ steps.policy.outputs.e2e_override }} steps: @@ -146,7 +146,7 @@ jobs: runs-on: ubuntu-22.04 permissions: contents: read - statuses: write + checks: write steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: @@ -169,7 +169,7 @@ jobs: runs-on: ubuntu-22.04 permissions: contents: read - statuses: write + checks: write steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: @@ -192,7 +192,7 @@ jobs: runs-on: ubuntu-22.04 permissions: contents: read - statuses: write + checks: write steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: @@ -458,7 +458,7 @@ jobs: - provision-servers permissions: contents: read - statuses: write + checks: write actions: read if: | !cancelled() && @@ -485,7 +485,7 @@ jobs: - provision-servers permissions: contents: read - statuses: write + checks: write actions: read if: | !cancelled() && @@ -533,7 +533,7 @@ jobs: - validate-maestro-flow-headers permissions: contents: read - statuses: write + checks: write actions: read if: | !cancelled() && @@ -562,7 +562,7 @@ jobs: - validate-maestro-flow-headers permissions: contents: read - statuses: write + checks: write actions: read if: | !cancelled() && diff --git a/.github/workflows/e2e-detox.yml b/.github/workflows/e2e-detox.yml index 547c5eba906..c6091e685ae 100644 --- a/.github/workflows/e2e-detox.yml +++ b/.github/workflows/e2e-detox.yml @@ -113,7 +113,7 @@ jobs: if: always() && (inputs.PLATFORM == 'ios' || inputs.PLATFORM == 'both') permissions: contents: read - statuses: write + checks: write needs: - run-ios-tests steps: @@ -133,7 +133,7 @@ jobs: || (needs.run-ios-tests.outputs.FAILURES == '0' && needs.run-ios-tests.outputs.SKIPPED > '0') && format('{0} passed, {1} skipped', needs.run-ios-tests.outputs.PASSES, needs.run-ios-tests.outputs.SKIPPED) || needs.run-ios-tests.outputs.FAILURES == '0' && format('All {0} tests passed', needs.run-ios-tests.outputs.TOTAL) || format('{0} passed, {1} failed, {2} skipped', needs.run-ios-tests.outputs.PASSES || '?', needs.run-ios-tests.outputs.FAILURES || '?', needs.run-ios-tests.outputs.SKIPPED || '0') }} - status: ${{ (needs.run-ios-tests.result == 'skipped' && 'failure') || needs.run-ios-tests.outputs.STATUS || 'failure' }} + status: ${{ (needs.run-ios-tests.result == 'skipped' && 'skipped') || needs.run-ios-tests.outputs.STATUS || 'failure' }} target_url: ${{ needs.run-ios-tests.outputs.TARGET_URL || format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }} update-final-status-android: @@ -141,7 +141,7 @@ jobs: if: always() && (inputs.PLATFORM == 'android' || inputs.PLATFORM == 'both') permissions: contents: read - statuses: write + checks: write needs: - run-android-tests steps: @@ -161,7 +161,7 @@ jobs: || (needs.run-android-tests.outputs.FAILURES == '0' && needs.run-android-tests.outputs.SKIPPED > '0') && format('{0} passed, {1} skipped', needs.run-android-tests.outputs.PASSES, needs.run-android-tests.outputs.SKIPPED) || needs.run-android-tests.outputs.FAILURES == '0' && format('All {0} tests passed', needs.run-android-tests.outputs.TOTAL) || format('{0} passed, {1} failed, {2} skipped', needs.run-android-tests.outputs.PASSES || '?', needs.run-android-tests.outputs.FAILURES || '?', needs.run-android-tests.outputs.SKIPPED || '0') }} - status: ${{ (needs.run-android-tests.result == 'skipped' && 'failure') || needs.run-android-tests.outputs.STATUS || 'failure' }} + status: ${{ (needs.run-android-tests.result == 'skipped' && 'skipped') || needs.run-android-tests.outputs.STATUS || 'failure' }} target_url: ${{ needs.run-android-tests.outputs.TARGET_URL || format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }} update-final-status-ipad: @@ -169,7 +169,7 @@ jobs: if: always() && (inputs.PLATFORM == 'ios' || inputs.PLATFORM == 'both') permissions: contents: read - statuses: write + checks: write needs: - run-ios-ipad-tests steps: @@ -189,5 +189,5 @@ jobs: || (needs.run-ios-ipad-tests.outputs.FAILURES == '0' && needs.run-ios-ipad-tests.outputs.SKIPPED > '0') && format('{0} passed, {1} skipped', needs.run-ios-ipad-tests.outputs.PASSES, needs.run-ios-ipad-tests.outputs.SKIPPED) || needs.run-ios-ipad-tests.outputs.FAILURES == '0' && format('All {0} tests passed', needs.run-ios-ipad-tests.outputs.TOTAL) || format('{0} passed, {1} failed, {2} skipped', needs.run-ios-ipad-tests.outputs.PASSES || '?', needs.run-ios-ipad-tests.outputs.FAILURES || '?', needs.run-ios-ipad-tests.outputs.SKIPPED || '0') }} - status: ${{ (needs.run-ios-ipad-tests.result == 'skipped' && 'failure') || needs.run-ios-ipad-tests.outputs.STATUS || 'failure' }} + status: ${{ (needs.run-ios-ipad-tests.result == 'skipped' && 'skipped') || needs.run-ios-ipad-tests.outputs.STATUS || 'failure' }} target_url: ${{ needs.run-ios-ipad-tests.outputs.TARGET_URL || format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }} diff --git a/.github/workflows/e2e-label-manager.yml b/.github/workflows/e2e-label-manager.yml index 196e55283e4..576032bcc61 100644 --- a/.github/workflows/e2e-label-manager.yml +++ b/.github/workflows/e2e-label-manager.yml @@ -47,7 +47,7 @@ jobs: runs-on: ubuntu-22.04 permissions: actions: write - statuses: write + checks: write issues: write pull-requests: write steps: @@ -176,7 +176,7 @@ jobs: issues: write pull-requests: write actions: write - statuses: write + checks: write steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 diff --git a/.github/workflows/e2e-maestro-pr.yml b/.github/workflows/e2e-maestro-pr.yml index cbb8692a859..4b6db5720fa 100644 --- a/.github/workflows/e2e-maestro-pr.yml +++ b/.github/workflows/e2e-maestro-pr.yml @@ -47,44 +47,40 @@ jobs: runs-on: ubuntu-22.04 permissions: contents: read - statuses: write + checks: write steps: - - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + ref: ${{ inputs.MOBILE_VERSION }} + - uses: ./.github/actions/update-commit-status env: - COMMIT_SHA: ${{ inputs.MOBILE_VERSION }} + GITHUB_TOKEN: ${{ github.token }} with: - script: | - const { owner, repo } = context.repo; - await github.rest.repos.createCommitStatus({ - owner, - repo, - sha: process.env.COMMIT_SHA, - state: 'pending', - context: 'e2e/maestro-ios-tests', - description: 'Maestro iOS tests for mattermost mobile app have started ...', - }); + repository_full_name: ${{ github.repository }} + commit_sha: ${{ inputs.MOBILE_VERSION }} + context: e2e/maestro-ios-tests + description: Maestro iOS tests for mattermost mobile app have started ... + status: pending update-initial-status-maestro-android: if: inputs.PLATFORM == 'android' || inputs.PLATFORM == 'both' runs-on: ubuntu-22.04 permissions: contents: read - statuses: write + checks: write steps: - - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + ref: ${{ inputs.MOBILE_VERSION }} + - uses: ./.github/actions/update-commit-status env: - COMMIT_SHA: ${{ inputs.MOBILE_VERSION }} + GITHUB_TOKEN: ${{ github.token }} with: - script: | - const { owner, repo } = context.repo; - await github.rest.repos.createCommitStatus({ - owner, - repo, - sha: process.env.COMMIT_SHA, - state: 'pending', - context: 'e2e/maestro-android-tests', - description: 'Maestro Android tests for mattermost mobile app have started ...', - }); + repository_full_name: ${{ github.repository }} + commit_sha: ${{ inputs.MOBILE_VERSION }} + context: e2e/maestro-android-tests + description: Maestro Android tests for mattermost mobile app have started ... + status: pending run-maestro-ios: name: Maestro iOS @@ -124,66 +120,54 @@ jobs: runs-on: ubuntu-22.04 permissions: contents: read - statuses: write + checks: write if: always() && (inputs.PLATFORM == 'ios' || inputs.PLATFORM == 'both') needs: - run-maestro-ios steps: - - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + ref: ${{ inputs.MOBILE_VERSION }} + - uses: ./.github/actions/update-commit-status env: - COMMIT_SHA: ${{ inputs.MOBILE_VERSION }} - STATUS: ${{ (needs.run-maestro-ios.result == 'skipped' && 'failure') || needs.run-maestro-ios.outputs.STATUS || 'failure' }} - TARGET_URL: ${{ needs.run-maestro-ios.outputs.TARGET_URL || format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }} - DESCRIPTION: >- + GITHUB_TOKEN: ${{ github.token }} + with: + repository_full_name: ${{ github.repository }} + commit_sha: ${{ inputs.MOBILE_VERSION }} + context: e2e/maestro-ios-tests + description: >- ${{ needs.run-maestro-ios.result == 'skipped' && 'Build failed — Maestro iOS tests did not run' || needs.run-maestro-ios.outputs.FAILURES == '-1' && 'All test machines failed before producing results' || (needs.run-maestro-ios.outputs.FAILURES == '0' && needs.run-maestro-ios.outputs.SKIPPED > '0') && format('{0} passed, {1} skipped', needs.run-maestro-ios.outputs.PASSES, needs.run-maestro-ios.outputs.SKIPPED) || needs.run-maestro-ios.outputs.FAILURES == '0' && format('All {0} tests passed', needs.run-maestro-ios.outputs.TOTAL) || format('{0} passed, {1} failed, {2} skipped', needs.run-maestro-ios.outputs.PASSES || '?', needs.run-maestro-ios.outputs.FAILURES || '?', needs.run-maestro-ios.outputs.SKIPPED || '0') }} - with: - script: | - const { owner, repo } = context.repo; - const targetUrl = process.env.TARGET_URL?.trim(); - await github.rest.repos.createCommitStatus({ - owner, - repo, - sha: process.env.COMMIT_SHA, - state: process.env.STATUS, - context: 'e2e/maestro-ios-tests', - description: (process.env.DESCRIPTION || '').substring(0, 140), - ...(targetUrl ? {target_url: targetUrl} : {}), - }); + status: ${{ (needs.run-maestro-ios.result == 'skipped' && 'skipped') || needs.run-maestro-ios.outputs.STATUS || 'failure' }} + target_url: ${{ needs.run-maestro-ios.outputs.TARGET_URL || format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }} update-final-status-maestro-android: runs-on: ubuntu-22.04 permissions: contents: read - statuses: write + checks: write if: always() && (inputs.PLATFORM == 'android' || inputs.PLATFORM == 'both') needs: - run-maestro-android steps: - - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + ref: ${{ inputs.MOBILE_VERSION }} + - uses: ./.github/actions/update-commit-status env: - COMMIT_SHA: ${{ inputs.MOBILE_VERSION }} - STATUS: ${{ (needs.run-maestro-android.result == 'skipped' && 'failure') || needs.run-maestro-android.outputs.STATUS || 'failure' }} - TARGET_URL: ${{ needs.run-maestro-android.outputs.TARGET_URL || format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }} - DESCRIPTION: >- + GITHUB_TOKEN: ${{ github.token }} + with: + repository_full_name: ${{ github.repository }} + commit_sha: ${{ inputs.MOBILE_VERSION }} + context: e2e/maestro-android-tests + description: >- ${{ needs.run-maestro-android.result == 'skipped' && 'Build failed — Maestro Android tests did not run' || needs.run-maestro-android.outputs.FAILURES == '-1' && 'All test machines failed before producing results' || (needs.run-maestro-android.outputs.FAILURES == '0' && needs.run-maestro-android.outputs.SKIPPED > '0') && format('{0} passed, {1} skipped', needs.run-maestro-android.outputs.PASSES, needs.run-maestro-android.outputs.SKIPPED) || needs.run-maestro-android.outputs.FAILURES == '0' && format('All {0} tests passed', needs.run-maestro-android.outputs.TOTAL) || format('{0} passed, {1} failed, {2} skipped', needs.run-maestro-android.outputs.PASSES || '?', needs.run-maestro-android.outputs.FAILURES || '?', needs.run-maestro-android.outputs.SKIPPED || '0') }} - with: - script: | - const { owner, repo } = context.repo; - const targetUrl = process.env.TARGET_URL?.trim(); - await github.rest.repos.createCommitStatus({ - owner, - repo, - sha: process.env.COMMIT_SHA, - state: process.env.STATUS, - context: 'e2e/maestro-android-tests', - description: (process.env.DESCRIPTION || '').substring(0, 140), - ...(targetUrl ? {target_url: targetUrl} : {}), - }); + status: ${{ (needs.run-maestro-android.result == 'skipped' && 'skipped') || needs.run-maestro-android.outputs.STATUS || 'failure' }} + target_url: ${{ needs.run-maestro-android.outputs.TARGET_URL || format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }} From 6d056e9822d668d0c3168f9c815971474ae602b9 Mon Sep 17 00:00:00 2001 From: yasserfaraazkhan Date: Tue, 30 Jun 2026 10:19:57 +0530 Subject: [PATCH 5/5] fix comments --- .github/actions/cancel-e2e-runs/action.yml | 32 ++++++++++++------- .../actions/update-commit-status/action.yml | 15 ++++++--- .github/workflows/e2e-maestro-pr.yml | 12 ++++--- 3 files changed, 39 insertions(+), 20 deletions(-) diff --git a/.github/actions/cancel-e2e-runs/action.yml b/.github/actions/cancel-e2e-runs/action.yml index f2329db0667..63c29256d77 100644 --- a/.github/actions/cancel-e2e-runs/action.yml +++ b/.github/actions/cancel-e2e-runs/action.yml @@ -95,22 +95,30 @@ runs: }; const markCommitStatusesCancelled = async (sha) => { + const skippedOutput = { + title: reason.substring(0, 140), + summary: reason.substring(0, 140), + }; for (const statusContext of E2E_STATUS_CONTEXTS) { try { - await github.rest.checks.create({ - owner, - repo, - name: statusContext, - head_sha: sha, - status: 'completed', - conclusion: 'skipped', - output: { - title: reason.substring(0, 140), - summary: reason.substring(0, 140), - }, + // Update the existing in-progress run if one exists; otherwise create. + const { data: { check_runs } } = await github.rest.checks.listForRef({ + owner, repo, ref: sha, check_name: statusContext, filter: 'latest', per_page: 1, }); + const existing = check_runs.find(r => r.status !== 'completed'); + if (existing) { + await github.rest.checks.update({ + owner, repo, check_run_id: existing.id, + status: 'completed', conclusion: 'skipped', output: skippedOutput, + }); + } else { + await github.rest.checks.create({ + owner, repo, name: statusContext, head_sha: sha, + status: 'completed', conclusion: 'skipped', output: skippedOutput, + }); + } } catch (err) { - core.warning(`checks.create ${statusContext} on ${sha}: ${err.message}`); + core.warning(`markCommitStatusesCancelled ${statusContext} on ${sha}: ${err.message}`); } } }; diff --git a/.github/actions/update-commit-status/action.yml b/.github/actions/update-commit-status/action.yml index 390879fb780..b2e6b3d26f0 100644 --- a/.github/actions/update-commit-status/action.yml +++ b/.github/actions/update-commit-status/action.yml @@ -45,6 +45,10 @@ runs: const isCompleted = status !== 'pending'; const conclusionMap = { success: 'success', failure: 'failure', error: 'failure', skipped: 'skipped' }; + // Use the workflow run ID as a stable external_id so the completion call + // targets exactly the check run this workflow created, not a concurrent one. + const externalId = String(context.runId); + const payload = { status: isCompleted ? 'completed' : 'in_progress', ...(isCompleted ? { conclusion: conclusionMap[status] } : {}), @@ -53,16 +57,18 @@ runs: }; if (isCompleted) { - // Update the existing in_progress run rather than creating a second orphaned one. + // Find the in-progress run that this workflow created (matched by external_id). const { data: { check_runs } } = await github.rest.checks.listForRef({ owner, repo, ref: process.env.COMMIT_SHA, check_name: process.env.CONTEXT, - filter: 'latest', - per_page: 1, + filter: 'all', + per_page: 100, }); - const existing = check_runs.find(r => r.status !== 'completed'); + const existing = check_runs.find( + r => r.external_id === externalId && r.status !== 'completed', + ); if (existing) { await github.rest.checks.update({ owner, repo, check_run_id: existing.id, ...payload }); return; @@ -74,5 +80,6 @@ runs: repo, name: process.env.CONTEXT, head_sha: process.env.COMMIT_SHA, + external_id: externalId, ...payload, }); diff --git a/.github/workflows/e2e-maestro-pr.yml b/.github/workflows/e2e-maestro-pr.yml index 4b6db5720fa..6dd24ceb9b0 100644 --- a/.github/workflows/e2e-maestro-pr.yml +++ b/.github/workflows/e2e-maestro-pr.yml @@ -51,7 +51,8 @@ jobs: steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: - ref: ${{ inputs.MOBILE_VERSION }} + ref: ${{ github.sha }} + persist-credentials: false - uses: ./.github/actions/update-commit-status env: GITHUB_TOKEN: ${{ github.token }} @@ -71,7 +72,8 @@ jobs: steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: - ref: ${{ inputs.MOBILE_VERSION }} + ref: ${{ github.sha }} + persist-credentials: false - uses: ./.github/actions/update-commit-status env: GITHUB_TOKEN: ${{ github.token }} @@ -127,7 +129,8 @@ jobs: steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: - ref: ${{ inputs.MOBILE_VERSION }} + ref: ${{ github.sha }} + persist-credentials: false - uses: ./.github/actions/update-commit-status env: GITHUB_TOKEN: ${{ github.token }} @@ -155,7 +158,8 @@ jobs: steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: - ref: ${{ inputs.MOBILE_VERSION }} + ref: ${{ github.sha }} + persist-credentials: false - uses: ./.github/actions/update-commit-status env: GITHUB_TOKEN: ${{ github.token }}