From d803d14215d9a3b6f9adc246d340847010a046be Mon Sep 17 00:00:00 2001 From: yasserfaraazkhan Date: Tue, 21 Jul 2026 18:21:37 +0530 Subject: [PATCH 01/10] chore(e2e): fix tsio reporting link and Do not post master report to release channel --- .github/workflows/compatibility-matrix-testing.yml | 1 - .github/workflows/e2e-functional.yml | 6 +++--- e2e/utils/cmt-channel-notify.js | 8 ++++---- e2e/utils/cmt-channel-notify.test.js | 10 +++++----- e2e/utils/tsio-report-status.js | 4 ++-- 5 files changed, 14 insertions(+), 15 deletions(-) diff --git a/.github/workflows/compatibility-matrix-testing.yml b/.github/workflows/compatibility-matrix-testing.yml index d2bb1258c2e..2938ed7f4e4 100644 --- a/.github/workflows/compatibility-matrix-testing.yml +++ b/.github/workflows/compatibility-matrix-testing.yml @@ -205,7 +205,6 @@ jobs: # Incoming webhook for the Desktop CMT/RC results channel (separate from # PR/master MM_DESKTOP_E2E_WEBHOOK_URL). Optional — when unset, TSIO # status still flips and channel notify is skipped. - # RC / CMT channel (same destination as master E2E via resolveWebhookUrl). MATTERMOST_CMT_WEBHOOK_URL: ${{ secrets.MM_E2E_RELEASE_WEBHOOK_URL }} # TSIO only sees test-case-level results — a job-level failure with no # matching failed test (hung worker teardown, crashed runner, npm ci diff --git a/.github/workflows/e2e-functional.yml b/.github/workflows/e2e-functional.yml index 37944879f1a..d3c0c34c47e 100644 --- a/.github/workflows/e2e-functional.yml +++ b/.github/workflows/e2e-functional.yml @@ -186,9 +186,9 @@ jobs: TSIO_POLL_DELAY_MS: 5000 COMMIT_STATUS_CONTEXT: e2e-test/desktop-playwright UPSTREAM_JOBS_SUCCEEDED: ${{ needs.e2e-tests.result == 'success' && needs.e2e-policy-tests.result == 'success' }} - # Master shares the CMT/RC channel; PR posts to the E2E QA channel. - # Routing is by compositeIdentity.name in cmt-channel-notify.js. - MATTERMOST_CMT_WEBHOOK_URL: ${{ secrets.MM_E2E_RELEASE_WEBHOOK_URL }} + # Master + PR → desktop E2E channel; CMT/RC uses MM_E2E_RELEASE_WEBHOOK_URL + # (wired only in compatibility-matrix-testing.yml). Routing is by + # compositeIdentity.name in cmt-channel-notify.js. MATTERMOST_E2E_WEBHOOK_URL: ${{ secrets.MM_DESKTOP_E2E_WEBHOOK_URL }} with: script: | diff --git a/e2e/utils/cmt-channel-notify.js b/e2e/utils/cmt-channel-notify.js index e8619e0496d..a15bca84476 100644 --- a/e2e/utils/cmt-channel-notify.js +++ b/e2e/utils/cmt-channel-notify.js @@ -88,8 +88,8 @@ function parseCmtJobName(jobName) { /** * Webhook routing: - * cmt-desktop + desktop-master → MM_E2E_RELEASE_WEBHOOK_URL (RC / master channel) - * desktop-pr → MM_DESKTOP_E2E_WEBHOOK_URL (PR channel) + * cmt-desktop → MM_E2E_RELEASE_WEBHOOK_URL (RC / CMT channel) + * desktop-master + desktop-pr → MM_DESKTOP_E2E_WEBHOOK_URL (PR / master channel) * * MATTERMOST_WEBHOOK_URL remains a fallback for workflows that set only one URL. * @@ -98,10 +98,10 @@ function parseCmtJobName(jobName) { * @returns {string} */ function resolveWebhookUrl(reportName, env = process.env) { - if (reportName === 'cmt-desktop' || reportName === 'desktop-master') { + if (reportName === 'cmt-desktop') { return env.MATTERMOST_CMT_WEBHOOK_URL || env.MATTERMOST_WEBHOOK_URL || ''; } - if (reportName === 'desktop-pr') { + if (reportName === 'desktop-master' || reportName === 'desktop-pr') { return env.MATTERMOST_E2E_WEBHOOK_URL || env.MATTERMOST_WEBHOOK_URL || ''; } return env.MATTERMOST_WEBHOOK_URL || ''; diff --git a/e2e/utils/cmt-channel-notify.test.js b/e2e/utils/cmt-channel-notify.test.js index d419dca8f48..5a790cc451d 100644 --- a/e2e/utils/cmt-channel-notify.test.js +++ b/e2e/utils/cmt-channel-notify.test.js @@ -81,18 +81,18 @@ describe('cmt-channel-notify', () => { MATTERMOST_WEBHOOK_URL: 'https://mm.example/hooks/fallback', }; - it('sends CMT and master to the CMT webhook', () => { + it('sends CMT to the release webhook', () => { assert.equal(resolveWebhookUrl('cmt-desktop', env), env.MATTERMOST_CMT_WEBHOOK_URL); - assert.equal(resolveWebhookUrl('desktop-master', env), env.MATTERMOST_CMT_WEBHOOK_URL); }); - it('sends PR runs to the E2E webhook', () => { + it('sends master and PR runs to the E2E webhook', () => { + assert.equal(resolveWebhookUrl('desktop-master', env), env.MATTERMOST_E2E_WEBHOOK_URL); assert.equal(resolveWebhookUrl('desktop-pr', env), env.MATTERMOST_E2E_WEBHOOK_URL); }); - it('does not fall back master to the PR webhook when CMT secret is missing', () => { + it('does not fall back CMT to the E2E webhook when release secret is missing', () => { assert.equal( - resolveWebhookUrl('desktop-master', {MATTERMOST_E2E_WEBHOOK_URL: env.MATTERMOST_E2E_WEBHOOK_URL}), + resolveWebhookUrl('cmt-desktop', {MATTERMOST_E2E_WEBHOOK_URL: env.MATTERMOST_E2E_WEBHOOK_URL}), '', ); }); diff --git a/e2e/utils/tsio-report-status.js b/e2e/utils/tsio-report-status.js index e722b06771e..7803ede8bbb 100644 --- a/e2e/utils/tsio-report-status.js +++ b/e2e/utils/tsio-report-status.js @@ -267,8 +267,8 @@ async function reportTsioStatus({ }); // Channel notify (best-effort). Routing (see resolveWebhookUrl): - // cmt-desktop / desktop-master → MM_E2E_RELEASE_WEBHOOK_URL - // desktop-pr → MM_DESKTOP_E2E_WEBHOOK_URL + // cmt-desktop → MM_E2E_RELEASE_WEBHOOK_URL + // desktop-master / desktop-pr → MM_DESKTOP_E2E_WEBHOOK_URL // Failures here must not undo a successfully written commit status. try { const notifyNames = new Set(['cmt-desktop', 'desktop-pr', 'desktop-master']); From 44fa8745eda44d45fbb892c2368cc29052ad91e6 Mon Sep 17 00:00:00 2001 From: yasserfaraazkhan Date: Tue, 21 Jul 2026 19:49:26 +0530 Subject: [PATCH 02/10] fix(e2e): fail closed on named TSIO webhook routing Named report groups (cmt/master/pr) use only their dedicated webhook secret so a missing secret never posts to the shared fallback channel. Co-authored-by: Cursor --- e2e/utils/cmt-channel-notify.js | 16 +++++++++------- e2e/utils/cmt-channel-notify.test.js | 25 +++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/e2e/utils/cmt-channel-notify.js b/e2e/utils/cmt-channel-notify.js index a15bca84476..eb27509642b 100644 --- a/e2e/utils/cmt-channel-notify.js +++ b/e2e/utils/cmt-channel-notify.js @@ -87,11 +87,13 @@ function parseCmtJobName(jobName) { } /** - * Webhook routing: - * cmt-desktop → MM_E2E_RELEASE_WEBHOOK_URL (RC / CMT channel) - * desktop-master + desktop-pr → MM_DESKTOP_E2E_WEBHOOK_URL (PR / master channel) + * Webhook routing (fail closed for named report groups): + * cmt-desktop → MATTERMOST_CMT_WEBHOOK_URL only + * desktop-master / desktop-pr → MATTERMOST_E2E_WEBHOOK_URL only * - * MATTERMOST_WEBHOOK_URL remains a fallback for workflows that set only one URL. + * Named groups never fall back to MATTERMOST_WEBHOOK_URL (avoids posting + * CMT/PR/master to the wrong channel when a dedicated secret is missing). + * Unknown names still use MATTERMOST_WEBHOOK_URL as a generic fallback. * * @param {string} reportName - compositeIdentity.name * @param {NodeJS.ProcessEnv} [env] @@ -99,10 +101,10 @@ function parseCmtJobName(jobName) { */ function resolveWebhookUrl(reportName, env = process.env) { if (reportName === 'cmt-desktop') { - return env.MATTERMOST_CMT_WEBHOOK_URL || env.MATTERMOST_WEBHOOK_URL || ''; + return env.MATTERMOST_CMT_WEBHOOK_URL || ''; } if (reportName === 'desktop-master' || reportName === 'desktop-pr') { - return env.MATTERMOST_E2E_WEBHOOK_URL || env.MATTERMOST_WEBHOOK_URL || ''; + return env.MATTERMOST_E2E_WEBHOOK_URL || ''; } return env.MATTERMOST_WEBHOOK_URL || ''; } @@ -451,7 +453,7 @@ async function fetchPerJobCountsFromConsolidated(baseUrl, compositeIdentity, gro */ async function postMattermostWebhook({core, webhookUrl, text, username = 'Desktop E2E'}) { if (!webhookUrl) { - core.info('MATTERMOST_WEBHOOK_URL not set — skipping E2E channel notify'); + core.info('Mattermost webhook URL not set — skipping E2E channel notify'); return; } diff --git a/e2e/utils/cmt-channel-notify.test.js b/e2e/utils/cmt-channel-notify.test.js index 5a790cc451d..700a389caa5 100644 --- a/e2e/utils/cmt-channel-notify.test.js +++ b/e2e/utils/cmt-channel-notify.test.js @@ -96,6 +96,31 @@ describe('cmt-channel-notify', () => { '', ); }); + + it('does not use the shared webhook when a dedicated CMT secret is missing', () => { + assert.equal( + resolveWebhookUrl('cmt-desktop', {MATTERMOST_WEBHOOK_URL: env.MATTERMOST_WEBHOOK_URL}), + '', + ); + }); + + it('does not use the shared webhook when a dedicated E2E secret is missing for master', () => { + assert.equal( + resolveWebhookUrl('desktop-master', {MATTERMOST_WEBHOOK_URL: env.MATTERMOST_WEBHOOK_URL}), + '', + ); + }); + + it('does not use the shared webhook when a dedicated E2E secret is missing for PR', () => { + assert.equal( + resolveWebhookUrl('desktop-pr', {MATTERMOST_WEBHOOK_URL: env.MATTERMOST_WEBHOOK_URL}), + '', + ); + }); + + it('still uses the shared webhook for unknown report names', () => { + assert.equal(resolveWebhookUrl('unknown-report', env), env.MATTERMOST_WEBHOOK_URL); + }); }); describe('formatCmtChannelMessage', () => { From 231873f2cf1b7d999268ebeedf1ebde7aa8c15d3 Mon Sep 17 00:00:00 2001 From: yasserfaraazkhan Date: Tue, 21 Jul 2026 19:52:55 +0530 Subject: [PATCH 03/10] chore(e2e): route desktop master reports to MM_E2E_MASTER_HEALTH_WEBHOOK_URL Master E2E channel notify uses the dedicated master-health webhook; PR keeps MM_DESKTOP_E2E_WEBHOOK_URL and CMT keeps the release webhook. Co-authored-by: Cursor --- .../workflows/compatibility-matrix-testing.yml | 4 ++-- .github/workflows/e2e-functional.yml | 7 ++++--- e2e/utils/cmt-channel-notify.js | 10 +++++++--- e2e/utils/cmt-channel-notify.test.js | 15 +++++++++++---- e2e/utils/tsio-report-status.js | 5 +++-- 5 files changed, 27 insertions(+), 14 deletions(-) diff --git a/.github/workflows/compatibility-matrix-testing.yml b/.github/workflows/compatibility-matrix-testing.yml index 2938ed7f4e4..d2a797a18fd 100644 --- a/.github/workflows/compatibility-matrix-testing.yml +++ b/.github/workflows/compatibility-matrix-testing.yml @@ -203,8 +203,8 @@ jobs: TSIO_POLL_DELAY_MS: 5000 COMMIT_STATUS_CONTEXT: e2e/compatibility-matrix-testing # Incoming webhook for the Desktop CMT/RC results channel (separate from - # PR/master MM_DESKTOP_E2E_WEBHOOK_URL). Optional — when unset, TSIO - # status still flips and channel notify is skipped. + # PR MM_DESKTOP_E2E_WEBHOOK_URL and master MM_E2E_MASTER_HEALTH_WEBHOOK_URL). + # Optional — when unset, TSIO status still flips and channel notify is skipped. MATTERMOST_CMT_WEBHOOK_URL: ${{ secrets.MM_E2E_RELEASE_WEBHOOK_URL }} # TSIO only sees test-case-level results — a job-level failure with no # matching failed test (hung worker teardown, crashed runner, npm ci diff --git a/.github/workflows/e2e-functional.yml b/.github/workflows/e2e-functional.yml index d3c0c34c47e..5c65b8b3c22 100644 --- a/.github/workflows/e2e-functional.yml +++ b/.github/workflows/e2e-functional.yml @@ -186,10 +186,11 @@ jobs: TSIO_POLL_DELAY_MS: 5000 COMMIT_STATUS_CONTEXT: e2e-test/desktop-playwright UPSTREAM_JOBS_SUCCEEDED: ${{ needs.e2e-tests.result == 'success' && needs.e2e-policy-tests.result == 'success' }} - # Master + PR → desktop E2E channel; CMT/RC uses MM_E2E_RELEASE_WEBHOOK_URL - # (wired only in compatibility-matrix-testing.yml). Routing is by - # compositeIdentity.name in cmt-channel-notify.js. + # PR → MM_DESKTOP_E2E_WEBHOOK_URL; master → MM_E2E_MASTER_HEALTH_WEBHOOK_URL; + # CMT/RC uses MM_E2E_RELEASE_WEBHOOK_URL (compatibility-matrix-testing.yml). + # Routing is by compositeIdentity.name in cmt-channel-notify.js. MATTERMOST_E2E_WEBHOOK_URL: ${{ secrets.MM_DESKTOP_E2E_WEBHOOK_URL }} + MATTERMOST_MASTER_HEALTH_WEBHOOK_URL: ${{ secrets.MM_E2E_MASTER_HEALTH_WEBHOOK_URL }} with: script: | const {reportUrl, status, stats} = await require('./e2e/utils/tsio-report-status.js')({ diff --git a/e2e/utils/cmt-channel-notify.js b/e2e/utils/cmt-channel-notify.js index eb27509642b..12e10265209 100644 --- a/e2e/utils/cmt-channel-notify.js +++ b/e2e/utils/cmt-channel-notify.js @@ -88,8 +88,9 @@ function parseCmtJobName(jobName) { /** * Webhook routing (fail closed for named report groups): - * cmt-desktop → MATTERMOST_CMT_WEBHOOK_URL only - * desktop-master / desktop-pr → MATTERMOST_E2E_WEBHOOK_URL only + * cmt-desktop → MATTERMOST_CMT_WEBHOOK_URL only (MM_E2E_RELEASE_WEBHOOK_URL) + * desktop-master → MATTERMOST_MASTER_HEALTH_WEBHOOK_URL only (MM_E2E_MASTER_HEALTH_WEBHOOK_URL) + * desktop-pr → MATTERMOST_E2E_WEBHOOK_URL only (MM_DESKTOP_E2E_WEBHOOK_URL) * * Named groups never fall back to MATTERMOST_WEBHOOK_URL (avoids posting * CMT/PR/master to the wrong channel when a dedicated secret is missing). @@ -103,7 +104,10 @@ function resolveWebhookUrl(reportName, env = process.env) { if (reportName === 'cmt-desktop') { return env.MATTERMOST_CMT_WEBHOOK_URL || ''; } - if (reportName === 'desktop-master' || reportName === 'desktop-pr') { + if (reportName === 'desktop-master') { + return env.MATTERMOST_MASTER_HEALTH_WEBHOOK_URL || ''; + } + if (reportName === 'desktop-pr') { return env.MATTERMOST_E2E_WEBHOOK_URL || ''; } return env.MATTERMOST_WEBHOOK_URL || ''; diff --git a/e2e/utils/cmt-channel-notify.test.js b/e2e/utils/cmt-channel-notify.test.js index 700a389caa5..82d33c251e0 100644 --- a/e2e/utils/cmt-channel-notify.test.js +++ b/e2e/utils/cmt-channel-notify.test.js @@ -78,6 +78,7 @@ describe('cmt-channel-notify', () => { const env = { MATTERMOST_CMT_WEBHOOK_URL: 'https://mm.example/hooks/cmt', MATTERMOST_E2E_WEBHOOK_URL: 'https://mm.example/hooks/e2e', + MATTERMOST_MASTER_HEALTH_WEBHOOK_URL: 'https://mm.example/hooks/master-health', MATTERMOST_WEBHOOK_URL: 'https://mm.example/hooks/fallback', }; @@ -85,8 +86,11 @@ describe('cmt-channel-notify', () => { assert.equal(resolveWebhookUrl('cmt-desktop', env), env.MATTERMOST_CMT_WEBHOOK_URL); }); - it('sends master and PR runs to the E2E webhook', () => { - assert.equal(resolveWebhookUrl('desktop-master', env), env.MATTERMOST_E2E_WEBHOOK_URL); + it('sends master runs to the master-health webhook', () => { + assert.equal(resolveWebhookUrl('desktop-master', env), env.MATTERMOST_MASTER_HEALTH_WEBHOOK_URL); + }); + + it('sends PR runs to the E2E webhook', () => { assert.equal(resolveWebhookUrl('desktop-pr', env), env.MATTERMOST_E2E_WEBHOOK_URL); }); @@ -104,9 +108,12 @@ describe('cmt-channel-notify', () => { ); }); - it('does not use the shared webhook when a dedicated E2E secret is missing for master', () => { + it('does not use the shared or PR webhook when master-health secret is missing', () => { assert.equal( - resolveWebhookUrl('desktop-master', {MATTERMOST_WEBHOOK_URL: env.MATTERMOST_WEBHOOK_URL}), + resolveWebhookUrl('desktop-master', { + MATTERMOST_WEBHOOK_URL: env.MATTERMOST_WEBHOOK_URL, + MATTERMOST_E2E_WEBHOOK_URL: env.MATTERMOST_E2E_WEBHOOK_URL, + }), '', ); }); diff --git a/e2e/utils/tsio-report-status.js b/e2e/utils/tsio-report-status.js index 7803ede8bbb..48e6b73e378 100644 --- a/e2e/utils/tsio-report-status.js +++ b/e2e/utils/tsio-report-status.js @@ -267,8 +267,9 @@ async function reportTsioStatus({ }); // Channel notify (best-effort). Routing (see resolveWebhookUrl): - // cmt-desktop → MM_E2E_RELEASE_WEBHOOK_URL - // desktop-master / desktop-pr → MM_DESKTOP_E2E_WEBHOOK_URL + // cmt-desktop → MM_E2E_RELEASE_WEBHOOK_URL + // desktop-master → MM_E2E_MASTER_HEALTH_WEBHOOK_URL + // desktop-pr → MM_DESKTOP_E2E_WEBHOOK_URL // Failures here must not undo a successfully written commit status. try { const notifyNames = new Set(['cmt-desktop', 'desktop-pr', 'desktop-master']); From a122503c81b4d856e54b5c8e76403688750d1503 Mon Sep 17 00:00:00 2001 From: yasserfaraazkhan Date: Tue, 21 Jul 2026 19:54:58 +0530 Subject: [PATCH 04/10] fix(e2e): accept MAIN alias for desktop master-health webhook routing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Canonicalize run_type MAIN→MASTER when building the TSIO identity name, and treat desktop-main like desktop-master for webhook/notify selection. Co-authored-by: Cursor --- .github/workflows/e2e-functional.yml | 4 ++++ e2e/utils/cmt-channel-notify.js | 9 +++++---- e2e/utils/cmt-channel-notify.test.js | 1 + e2e/utils/tsio-report-status.js | 6 +++--- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/.github/workflows/e2e-functional.yml b/.github/workflows/e2e-functional.yml index 5c65b8b3c22..f4658b2003e 100644 --- a/.github/workflows/e2e-functional.yml +++ b/.github/workflows/e2e-functional.yml @@ -83,6 +83,10 @@ jobs: # platform legs (linux/macos/windows) + policy-tests-macos + policy-tests-windows TOTAL=$(( $(echo "${PLATFORMS}" | jq -c 'length') + 2 )) echo "total-reports-expected=${TOTAL}" >> "$GITHUB_OUTPUT" + # Accept MAIN as an alias for MASTER (mobile-style); Matterwick sends MASTER. + if [ "${RUN_TYPE}" = "MAIN" ]; then + RUN_TYPE=MASTER + fi NAME="desktop-$(echo "${RUN_TYPE}" | tr '[:upper:]' '[:lower:]')" if [ -n "${PR_NUMBER}" ]; then COMPOSITE_IDENTITY=$(jq -nc \ diff --git a/e2e/utils/cmt-channel-notify.js b/e2e/utils/cmt-channel-notify.js index 12e10265209..da97c655ba1 100644 --- a/e2e/utils/cmt-channel-notify.js +++ b/e2e/utils/cmt-channel-notify.js @@ -88,9 +88,9 @@ function parseCmtJobName(jobName) { /** * Webhook routing (fail closed for named report groups): - * cmt-desktop → MATTERMOST_CMT_WEBHOOK_URL only (MM_E2E_RELEASE_WEBHOOK_URL) - * desktop-master → MATTERMOST_MASTER_HEALTH_WEBHOOK_URL only (MM_E2E_MASTER_HEALTH_WEBHOOK_URL) - * desktop-pr → MATTERMOST_E2E_WEBHOOK_URL only (MM_DESKTOP_E2E_WEBHOOK_URL) + * cmt-desktop → MATTERMOST_CMT_WEBHOOK_URL only (MM_E2E_RELEASE_WEBHOOK_URL) + * desktop-master/desktop-main → MATTERMOST_MASTER_HEALTH_WEBHOOK_URL only (MM_E2E_MASTER_HEALTH_WEBHOOK_URL) + * desktop-pr → MATTERMOST_E2E_WEBHOOK_URL only (MM_DESKTOP_E2E_WEBHOOK_URL) * * Named groups never fall back to MATTERMOST_WEBHOOK_URL (avoids posting * CMT/PR/master to the wrong channel when a dedicated secret is missing). @@ -104,7 +104,7 @@ function resolveWebhookUrl(reportName, env = process.env) { if (reportName === 'cmt-desktop') { return env.MATTERMOST_CMT_WEBHOOK_URL || ''; } - if (reportName === 'desktop-master') { + if (reportName === 'desktop-master' || reportName === 'desktop-main') { return env.MATTERMOST_MASTER_HEALTH_WEBHOOK_URL || ''; } if (reportName === 'desktop-pr') { @@ -244,6 +244,7 @@ function reportTitleForIdentity(compositeIdentity) { case 'desktop-pr': return 'Desktop PR E2E'; case 'desktop-master': + case 'desktop-main': return 'Desktop Master E2E'; default: return 'Desktop E2E'; diff --git a/e2e/utils/cmt-channel-notify.test.js b/e2e/utils/cmt-channel-notify.test.js index 82d33c251e0..d46a39e9ba7 100644 --- a/e2e/utils/cmt-channel-notify.test.js +++ b/e2e/utils/cmt-channel-notify.test.js @@ -88,6 +88,7 @@ describe('cmt-channel-notify', () => { it('sends master runs to the master-health webhook', () => { assert.equal(resolveWebhookUrl('desktop-master', env), env.MATTERMOST_MASTER_HEALTH_WEBHOOK_URL); + assert.equal(resolveWebhookUrl('desktop-main', env), env.MATTERMOST_MASTER_HEALTH_WEBHOOK_URL); }); it('sends PR runs to the E2E webhook', () => { diff --git a/e2e/utils/tsio-report-status.js b/e2e/utils/tsio-report-status.js index 48e6b73e378..97d18b02597 100644 --- a/e2e/utils/tsio-report-status.js +++ b/e2e/utils/tsio-report-status.js @@ -268,11 +268,11 @@ async function reportTsioStatus({ // Channel notify (best-effort). Routing (see resolveWebhookUrl): // cmt-desktop → MM_E2E_RELEASE_WEBHOOK_URL - // desktop-master → MM_E2E_MASTER_HEALTH_WEBHOOK_URL - // desktop-pr → MM_DESKTOP_E2E_WEBHOOK_URL + // desktop-master / desktop-main → MM_E2E_MASTER_HEALTH_WEBHOOK_URL + // desktop-pr → MM_DESKTOP_E2E_WEBHOOK_URL // Failures here must not undo a successfully written commit status. try { - const notifyNames = new Set(['cmt-desktop', 'desktop-pr', 'desktop-master']); + const notifyNames = new Set(['cmt-desktop', 'desktop-pr', 'desktop-master', 'desktop-main']); if (notifyNames.has(compositeIdentity.name)) { const {notifyCmtChannel, resolveWebhookUrl} = require('./cmt-channel-notify.js'); const webhookUrl = resolveWebhookUrl(compositeIdentity.name); From 5a7544bd812c9c5518c1a22d0b895dfaaef170df Mon Sep 17 00:00:00 2001 From: yasserfaraazkhan Date: Tue, 21 Jul 2026 19:56:38 +0530 Subject: [PATCH 05/10] revert(e2e): drop mistaken desktop-main / MAIN alias MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Desktop master pushes are Matterwick MASTER → desktop-master only. Mobile keeps MAIN/MASTER→mobile-main; that does not apply to desktop. Co-authored-by: Cursor --- .github/workflows/e2e-functional.yml | 4 ---- e2e/utils/cmt-channel-notify.js | 9 ++++----- e2e/utils/cmt-channel-notify.test.js | 1 - e2e/utils/tsio-report-status.js | 6 +++--- 4 files changed, 7 insertions(+), 13 deletions(-) diff --git a/.github/workflows/e2e-functional.yml b/.github/workflows/e2e-functional.yml index f4658b2003e..5c65b8b3c22 100644 --- a/.github/workflows/e2e-functional.yml +++ b/.github/workflows/e2e-functional.yml @@ -83,10 +83,6 @@ jobs: # platform legs (linux/macos/windows) + policy-tests-macos + policy-tests-windows TOTAL=$(( $(echo "${PLATFORMS}" | jq -c 'length') + 2 )) echo "total-reports-expected=${TOTAL}" >> "$GITHUB_OUTPUT" - # Accept MAIN as an alias for MASTER (mobile-style); Matterwick sends MASTER. - if [ "${RUN_TYPE}" = "MAIN" ]; then - RUN_TYPE=MASTER - fi NAME="desktop-$(echo "${RUN_TYPE}" | tr '[:upper:]' '[:lower:]')" if [ -n "${PR_NUMBER}" ]; then COMPOSITE_IDENTITY=$(jq -nc \ diff --git a/e2e/utils/cmt-channel-notify.js b/e2e/utils/cmt-channel-notify.js index da97c655ba1..12e10265209 100644 --- a/e2e/utils/cmt-channel-notify.js +++ b/e2e/utils/cmt-channel-notify.js @@ -88,9 +88,9 @@ function parseCmtJobName(jobName) { /** * Webhook routing (fail closed for named report groups): - * cmt-desktop → MATTERMOST_CMT_WEBHOOK_URL only (MM_E2E_RELEASE_WEBHOOK_URL) - * desktop-master/desktop-main → MATTERMOST_MASTER_HEALTH_WEBHOOK_URL only (MM_E2E_MASTER_HEALTH_WEBHOOK_URL) - * desktop-pr → MATTERMOST_E2E_WEBHOOK_URL only (MM_DESKTOP_E2E_WEBHOOK_URL) + * cmt-desktop → MATTERMOST_CMT_WEBHOOK_URL only (MM_E2E_RELEASE_WEBHOOK_URL) + * desktop-master → MATTERMOST_MASTER_HEALTH_WEBHOOK_URL only (MM_E2E_MASTER_HEALTH_WEBHOOK_URL) + * desktop-pr → MATTERMOST_E2E_WEBHOOK_URL only (MM_DESKTOP_E2E_WEBHOOK_URL) * * Named groups never fall back to MATTERMOST_WEBHOOK_URL (avoids posting * CMT/PR/master to the wrong channel when a dedicated secret is missing). @@ -104,7 +104,7 @@ function resolveWebhookUrl(reportName, env = process.env) { if (reportName === 'cmt-desktop') { return env.MATTERMOST_CMT_WEBHOOK_URL || ''; } - if (reportName === 'desktop-master' || reportName === 'desktop-main') { + if (reportName === 'desktop-master') { return env.MATTERMOST_MASTER_HEALTH_WEBHOOK_URL || ''; } if (reportName === 'desktop-pr') { @@ -244,7 +244,6 @@ function reportTitleForIdentity(compositeIdentity) { case 'desktop-pr': return 'Desktop PR E2E'; case 'desktop-master': - case 'desktop-main': return 'Desktop Master E2E'; default: return 'Desktop E2E'; diff --git a/e2e/utils/cmt-channel-notify.test.js b/e2e/utils/cmt-channel-notify.test.js index d46a39e9ba7..82d33c251e0 100644 --- a/e2e/utils/cmt-channel-notify.test.js +++ b/e2e/utils/cmt-channel-notify.test.js @@ -88,7 +88,6 @@ describe('cmt-channel-notify', () => { it('sends master runs to the master-health webhook', () => { assert.equal(resolveWebhookUrl('desktop-master', env), env.MATTERMOST_MASTER_HEALTH_WEBHOOK_URL); - assert.equal(resolveWebhookUrl('desktop-main', env), env.MATTERMOST_MASTER_HEALTH_WEBHOOK_URL); }); it('sends PR runs to the E2E webhook', () => { diff --git a/e2e/utils/tsio-report-status.js b/e2e/utils/tsio-report-status.js index 97d18b02597..48e6b73e378 100644 --- a/e2e/utils/tsio-report-status.js +++ b/e2e/utils/tsio-report-status.js @@ -268,11 +268,11 @@ async function reportTsioStatus({ // Channel notify (best-effort). Routing (see resolveWebhookUrl): // cmt-desktop → MM_E2E_RELEASE_WEBHOOK_URL - // desktop-master / desktop-main → MM_E2E_MASTER_HEALTH_WEBHOOK_URL - // desktop-pr → MM_DESKTOP_E2E_WEBHOOK_URL + // desktop-master → MM_E2E_MASTER_HEALTH_WEBHOOK_URL + // desktop-pr → MM_DESKTOP_E2E_WEBHOOK_URL // Failures here must not undo a successfully written commit status. try { - const notifyNames = new Set(['cmt-desktop', 'desktop-pr', 'desktop-master', 'desktop-main']); + const notifyNames = new Set(['cmt-desktop', 'desktop-pr', 'desktop-master']); if (notifyNames.has(compositeIdentity.name)) { const {notifyCmtChannel, resolveWebhookUrl} = require('./cmt-channel-notify.js'); const webhookUrl = resolveWebhookUrl(compositeIdentity.name); From 1efdec3c7a3f0b2febe5c4b230a81eaa038e7ff3 Mon Sep 17 00:00:00 2001 From: yasserfaraazkhan Date: Wed, 22 Jul 2026 00:02:56 +0530 Subject: [PATCH 06/10] ci(e2e): point TSIO uploads/status at staging for #81 validation Pin report-upload to main@b5580f4 and enable useStaging so desktop E2E hits staging-test-io after the staging deploy of the Maestro/Detox ingest fix. Co-authored-by: Cursor --- .github/workflows/compatibility-matrix-testing.yml | 1 + .github/workflows/e2e-functional-template.yml | 3 ++- .github/workflows/e2e-functional.yml | 4 +++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/compatibility-matrix-testing.yml b/.github/workflows/compatibility-matrix-testing.yml index d2a797a18fd..cc72f6b96e9 100644 --- a/.github/workflows/compatibility-matrix-testing.yml +++ b/.github/workflows/compatibility-matrix-testing.yml @@ -219,6 +219,7 @@ jobs: commitStatusContext: process.env.COMMIT_STATUS_CONTEXT, upstreamJobsSucceeded: process.env.UPSTREAM_JOBS_SUCCEEDED === 'true', failOnTestFailures: true, + useStaging: true, }); core.info(`TSIO report ${reportUrl}: ${status} (${JSON.stringify(stats)})`); diff --git a/.github/workflows/e2e-functional-template.yml b/.github/workflows/e2e-functional-template.yml index 1256f917c5a..6bf415b91fc 100644 --- a/.github/workflows/e2e-functional-template.yml +++ b/.github/workflows/e2e-functional-template.yml @@ -303,8 +303,9 @@ jobs: - name: e2e/upload-report-to-tsio if: ${{ always() && inputs.tsio-config != '' && hashFiles('e2e/test-results/results.json') != '' }} continue-on-error: true - uses: mattermost/mattermost-test-system-io/.github/actions/test-system-io-report-upload@606431efb597481b0857066be8130e86c22d2a03 # release-0.11.0 / 2026-07-21 + uses: mattermost/mattermost-test-system-io/.github/actions/test-system-io-report-upload@b5580f407a2776c064c626cc3c42295f2111fdc7 # staging validation — main@b5580f4 (#81 Maestro FilePath + Detox paths) with: + use-staging: true composite-identity: ${{ toJSON(fromJSON(inputs.tsio-config).composite_identity) }} total-reports-expected: ${{ fromJSON(inputs.tsio-config).total_reports_expected }} framework: playwright diff --git a/.github/workflows/e2e-functional.yml b/.github/workflows/e2e-functional.yml index 5c65b8b3c22..d60309179e4 100644 --- a/.github/workflows/e2e-functional.yml +++ b/.github/workflows/e2e-functional.yml @@ -200,6 +200,7 @@ jobs: upstreamJobsSucceeded: process.env.UPSTREAM_JOBS_SUCCEEDED === 'true', commitStatusContext: process.env.COMMIT_STATUS_CONTEXT, failOnTestFailures: true, + useStaging: true, }); core.info(`TSIO report ${reportUrl}: ${status} (${JSON.stringify(stats)})`); @@ -435,8 +436,9 @@ jobs: - name: e2e/upload-report-to-tsio if: ${{ always() && needs.prepare-matrix.outputs.tsio-composite-identity != '' && hashFiles('e2e/test-results/results.json') != '' }} continue-on-error: true - uses: mattermost/mattermost-test-system-io/.github/actions/test-system-io-report-upload@606431efb597481b0857066be8130e86c22d2a03 # release-0.11.0 / 2026-07-21 + uses: mattermost/mattermost-test-system-io/.github/actions/test-system-io-report-upload@b5580f407a2776c064c626cc3c42295f2111fdc7 # staging validation — main@b5580f4 (#81 Maestro FilePath + Detox paths) with: + use-staging: true composite-identity: ${{ needs.prepare-matrix.outputs.tsio-composite-identity }} total-reports-expected: ${{ needs.prepare-matrix.outputs.tsio-total-reports-expected }} framework: playwright From 6849e17796fabf71e2c4e5ea7d56d42ec6c10867 Mon Sep 17 00:00:00 2001 From: yasserfaraazkhan Date: Wed, 22 Jul 2026 13:11:07 +0530 Subject: [PATCH 07/10] fix(e2e): harden MM-T4049 settings chrome on Linux after tiling Skip the flaky settings category click that tears down the Linux WebContentsView mid-action; open/close via cancelModal like focus tests, and settle briefly after tiling before interacting. Co-authored-by: Cursor --- e2e/specs/startup/window_position.test.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/e2e/specs/startup/window_position.test.ts b/e2e/specs/startup/window_position.test.ts index 37ee210a40d..181fca99bd4 100644 --- a/e2e/specs/startup/window_position.test.ts +++ b/e2e/specs/startup/window_position.test.ts @@ -152,8 +152,20 @@ async function exerciseWindowChrome( } if (openSettings) { + // Open + cancelModal exercises settings chrome while tiled/fullscreen. + // Avoid clicking category buttons here: on Linux CI, after parent tiling the + // settings WebContentsView can tear down mid-click ("Target page ... closed"). + // focus.test uses the same cancelModal close path successfully. const settingsWindow = await openSettingsWindow(electronApp); - await settingsWindow.click('#settingCategoryButton-general'); + await settingsWindow.waitForSelector('.SettingsModal', {timeout: 15_000}); + if (process.platform === 'linux') { + // ModalView defers setBounds by 10ms on Linux; wait for the view to settle + // after the parent was tiled before touching the page. + await new Promise((resolve) => setTimeout(resolve, 300)); + } + if (settingsWindow.isClosed()) { + throw new Error('Settings window closed before cancelModal could run'); + } await settingsWindow.evaluate(() => { const desktop = (window as Window & {desktop?: {modals?: {cancelModal?: () => void}}}).desktop; if (!desktop?.modals?.cancelModal) { @@ -186,6 +198,8 @@ test.describe('startup/window_position', () => { await mainWindow.waitForSelector('#newTabButton', {timeout: 30_000}); const tiledBounds = await tileMainWindowToLeftHalf(electronApp); + // Let MAIN_WINDOW_RESIZED / modal bound handlers settle before opening settings. + await new Promise((resolve) => setTimeout(resolve, 300)); await exerciseWindowChrome(electronApp, mainWindow); const afterTiled = await getMainWindowState(electronApp); From d69a9e246036e52c794d981ab61ba302a872b78d Mon Sep 17 00:00:00 2001 From: yasserfaraazkhan Date: Wed, 22 Jul 2026 13:33:21 +0530 Subject: [PATCH 08/10] fix(e2e): add blank line before comment for lines-around-comment Co-authored-by: Cursor --- e2e/specs/startup/window_position.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/e2e/specs/startup/window_position.test.ts b/e2e/specs/startup/window_position.test.ts index 181fca99bd4..b2f6f3809d1 100644 --- a/e2e/specs/startup/window_position.test.ts +++ b/e2e/specs/startup/window_position.test.ts @@ -198,6 +198,7 @@ test.describe('startup/window_position', () => { await mainWindow.waitForSelector('#newTabButton', {timeout: 30_000}); const tiledBounds = await tileMainWindowToLeftHalf(electronApp); + // Let MAIN_WINDOW_RESIZED / modal bound handlers settle before opening settings. await new Promise((resolve) => setTimeout(resolve, 300)); await exerciseWindowChrome(electronApp, mainWindow); From 9d4c1ea196eb8df77cdf21153a0b08ffc13c70c9 Mon Sep 17 00:00:00 2001 From: yasserfaraazkhan Date: Wed, 22 Jul 2026 13:38:13 +0530 Subject: [PATCH 09/10] ci(e2e): flip TSIO uploads/status from staging to production Pin report-upload to release-0.12.0 and drop use-staging after #81 validation. Co-authored-by: Cursor --- .github/workflows/compatibility-matrix-testing.yml | 1 - .github/workflows/e2e-functional-template.yml | 3 +-- .github/workflows/e2e-functional.yml | 4 +--- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/compatibility-matrix-testing.yml b/.github/workflows/compatibility-matrix-testing.yml index cc72f6b96e9..d2a797a18fd 100644 --- a/.github/workflows/compatibility-matrix-testing.yml +++ b/.github/workflows/compatibility-matrix-testing.yml @@ -219,7 +219,6 @@ jobs: commitStatusContext: process.env.COMMIT_STATUS_CONTEXT, upstreamJobsSucceeded: process.env.UPSTREAM_JOBS_SUCCEEDED === 'true', failOnTestFailures: true, - useStaging: true, }); core.info(`TSIO report ${reportUrl}: ${status} (${JSON.stringify(stats)})`); diff --git a/.github/workflows/e2e-functional-template.yml b/.github/workflows/e2e-functional-template.yml index 6bf415b91fc..4b8ece4fffb 100644 --- a/.github/workflows/e2e-functional-template.yml +++ b/.github/workflows/e2e-functional-template.yml @@ -303,9 +303,8 @@ jobs: - name: e2e/upload-report-to-tsio if: ${{ always() && inputs.tsio-config != '' && hashFiles('e2e/test-results/results.json') != '' }} continue-on-error: true - uses: mattermost/mattermost-test-system-io/.github/actions/test-system-io-report-upload@b5580f407a2776c064c626cc3c42295f2111fdc7 # staging validation — main@b5580f4 (#81 Maestro FilePath + Detox paths) + uses: mattermost/mattermost-test-system-io/.github/actions/test-system-io-report-upload@45fb048848edc392f398144f45e2f3e30f43aeaa # release-0.12.0 / main@45fb048 (#81 Maestro FilePath + Detox paths, #82 background finalize) with: - use-staging: true composite-identity: ${{ toJSON(fromJSON(inputs.tsio-config).composite_identity) }} total-reports-expected: ${{ fromJSON(inputs.tsio-config).total_reports_expected }} framework: playwright diff --git a/.github/workflows/e2e-functional.yml b/.github/workflows/e2e-functional.yml index d60309179e4..2aabcd2d327 100644 --- a/.github/workflows/e2e-functional.yml +++ b/.github/workflows/e2e-functional.yml @@ -200,7 +200,6 @@ jobs: upstreamJobsSucceeded: process.env.UPSTREAM_JOBS_SUCCEEDED === 'true', commitStatusContext: process.env.COMMIT_STATUS_CONTEXT, failOnTestFailures: true, - useStaging: true, }); core.info(`TSIO report ${reportUrl}: ${status} (${JSON.stringify(stats)})`); @@ -436,9 +435,8 @@ jobs: - name: e2e/upload-report-to-tsio if: ${{ always() && needs.prepare-matrix.outputs.tsio-composite-identity != '' && hashFiles('e2e/test-results/results.json') != '' }} continue-on-error: true - uses: mattermost/mattermost-test-system-io/.github/actions/test-system-io-report-upload@b5580f407a2776c064c626cc3c42295f2111fdc7 # staging validation — main@b5580f4 (#81 Maestro FilePath + Detox paths) + uses: mattermost/mattermost-test-system-io/.github/actions/test-system-io-report-upload@45fb048848edc392f398144f45e2f3e30f43aeaa # release-0.12.0 / main@45fb048 (#81 Maestro FilePath + Detox paths, #82 background finalize) with: - use-staging: true composite-identity: ${{ needs.prepare-matrix.outputs.tsio-composite-identity }} total-reports-expected: ${{ needs.prepare-matrix.outputs.tsio-total-reports-expected }} framework: playwright From 612d51239ee05e7182c5f2801ed99707f43da52a Mon Sep 17 00:00:00 2001 From: yasserfaraazkhan Date: Wed, 22 Jul 2026 14:38:23 +0530 Subject: [PATCH 10/10] revert(e2e): undo MM-T4049 window_position harden that caused 21 failures Restore settings chrome exercise to click #settingCategoryButton-general and drop the Linux settle sleeps from 6849e177/d69a9e24. Co-authored-by: Cursor --- e2e/specs/startup/window_position.test.ts | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/e2e/specs/startup/window_position.test.ts b/e2e/specs/startup/window_position.test.ts index b2f6f3809d1..37ee210a40d 100644 --- a/e2e/specs/startup/window_position.test.ts +++ b/e2e/specs/startup/window_position.test.ts @@ -152,20 +152,8 @@ async function exerciseWindowChrome( } if (openSettings) { - // Open + cancelModal exercises settings chrome while tiled/fullscreen. - // Avoid clicking category buttons here: on Linux CI, after parent tiling the - // settings WebContentsView can tear down mid-click ("Target page ... closed"). - // focus.test uses the same cancelModal close path successfully. const settingsWindow = await openSettingsWindow(electronApp); - await settingsWindow.waitForSelector('.SettingsModal', {timeout: 15_000}); - if (process.platform === 'linux') { - // ModalView defers setBounds by 10ms on Linux; wait for the view to settle - // after the parent was tiled before touching the page. - await new Promise((resolve) => setTimeout(resolve, 300)); - } - if (settingsWindow.isClosed()) { - throw new Error('Settings window closed before cancelModal could run'); - } + await settingsWindow.click('#settingCategoryButton-general'); await settingsWindow.evaluate(() => { const desktop = (window as Window & {desktop?: {modals?: {cancelModal?: () => void}}}).desktop; if (!desktop?.modals?.cancelModal) { @@ -198,9 +186,6 @@ test.describe('startup/window_position', () => { await mainWindow.waitForSelector('#newTabButton', {timeout: 30_000}); const tiledBounds = await tileMainWindowToLeftHalf(electronApp); - - // Let MAIN_WINDOW_RESIZED / modal bound handlers settle before opening settings. - await new Promise((resolve) => setTimeout(resolve, 300)); await exerciseWindowChrome(electronApp, mainWindow); const afterTiled = await getMainWindowState(electronApp);