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

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

---------

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

168 lines
7.4 KiB
YAML

# yaml-language-server: $schema=https://json.schemastore.org/github-action.json
name: Publish E2E/Override commit statuses
description: >-
Unblock the required e2e-test/* commit statuses for a SHA so a PR carrying
E2E/Override (or one that skipped E2E entirely) can merge under required
checks.
Only touches contexts that are actually blocking — pending, failing, or with
no status yet. A context that is genuinely green keeps its own result and
description, so the checks list still shows what really passed instead of
reporting every platform as overridden.
A failing context keeps its real pass/fail counts and gains an "- e2e
overridden" suffix, so the check reads "522 passed, 1 failed, 81 skipped -
e2e overridden" rather than hiding what failed.
Covers all three override paths: E2E never dispatched, E2E already failed
(statuses are keyed by SHA, so this overwrites the failures), and E2E still
running. In-flight runs are never cancelled — their finalize step re-checks
the label (.github/actions/e2e-override-label) so a late result cannot re-red
a check the maintainer deliberately bypassed.
inputs:
commit_sha:
description: Commit SHA to post statuses on (the PR head SHA)
required: true
state:
description: >-
Commit status state (success | pending). Use pending to re-block the PR
when E2E/Override is removed, so the waiver does not linger on the SHA.
pending only resets contexts this action previously overrode.
required: false
default: success
description:
description: >-
Commit status description for contexts that were pending or had no status.
A failing context keeps its own description plus an override suffix.
required: false
default: e2e overridden
target_url:
description: Link shown on the check
required: false
default: ''
runs:
using: composite
steps:
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
COMMIT_SHA: ${{ inputs.commit_sha }}
STATE: ${{ inputs.state }}
DESCRIPTION: ${{ inputs.description }}
TARGET_URL: ${{ inputs.target_url }}
with:
script: |
const owner = context.repo.owner;
const repo = context.repo.repo;
const sha = process.env.COMMIT_SHA;
const state = process.env.STATE || 'success';
const description = (process.env.DESCRIPTION || '').substring(0, 140);
const targetUrl = process.env.TARGET_URL?.trim();
if (!sha) {
throw new Error('e2e-override-status: commit_sha is required');
}
// Relative requires resolve against the workspace, so the repo must be
// checked out before this action runs.
const {E2E_STATUS_CONTEXTS} = require('./detox/utils/build-tsio-job-config.js');
// Present in every success description this action writes, so the
// pending path can reset exactly the contexts it overrode and never a
// platform that passed on its own merits.
const OVERRIDE_MARKER = 'e2e overridden';
// getCombinedStatusForRef caps statuses at 100 per page. This repo only
// has 5 required contexts today, but other tooling can post legacy
// statuses on the same SHA — paginate so a busy commit can't silently
// hide one of the 5 required contexts past page 1.
const statuses = [];
for (let page = 1; ; page++) {
const {data: combined} = await github.rest.repos.getCombinedStatusForRef({
owner, repo, ref: sha, per_page: 100, page,
});
statuses.push(...combined.statuses);
if (combined.statuses.length < 100) {
break;
}
}
// Only the latest status per context matters — this is current state,
// not history.
const latestByContext = new Map(
statuses.map((status) => [status.context, status]),
);
const failures = [];
let touched = 0;
for (const statusContext of E2E_STATUS_CONTEXTS) {
const latest = latestByContext.get(statusContext);
const latestDescription = latest?.description || '';
let statusDescription;
let statusTargetUrl;
if (state === 'pending') {
// Undo only what this action did. A context that never carried the
// override — including one that failed on its own after a later
// push — keeps its real result.
if (!latestDescription.includes(OVERRIDE_MARKER)) {
continue;
}
statusDescription = description;
statusTargetUrl = targetUrl;
} else if (latest?.state === 'success') {
// Nothing to unblock: either genuinely green — leave the real result
// and its counts alone — or already overridden, in which case
// re-posting would only replace a description carrying the real
// counts with a bare one.
continue;
} else if (latest?.state === 'failure' || latest?.state === 'error') {
// Keep the real counts so the check still says what failed.
// Reserve room for the suffix before truncating the base: TSIO
// descriptions are already capped at 140 (decideStatus in
// tsio-report-status.js), and truncating after concatenation could
// cut the marker off a long one, leaving the pending path unable to
// recognise this context as overridden.
const suffix = ` - ${OVERRIDE_MARKER}`;
const base = latestDescription.substring(0, Math.max(0, 140 - suffix.length));
statusDescription = `${base}${suffix}`.substring(0, 140);
// Keep the link to the real report — that is what a reviewer needs
// to judge whether the bypass was reasonable, not this action's run.
statusTargetUrl = latest.target_url || targetUrl;
} else {
// Pending, or no status at all (E2E never dispatched / new SHA).
statusDescription = description;
statusTargetUrl = targetUrl;
}
touched++;
try {
await github.rest.repos.createCommitStatus({
owner,
repo,
sha,
state,
context: statusContext,
description: statusDescription,
...(statusTargetUrl ? {target_url: statusTargetUrl} : {}),
});
core.info(`Published ${state} for ${statusContext} on ${sha}`);
} catch (err) {
core.warning(`createCommitStatus ${statusContext} on ${sha}: ${err.message}`);
failures.push(`${statusContext}: ${err.message}`);
}
}
if (touched === 0) {
core.info(
state === 'pending' ?
`No e2e-test/* contexts carry the override marker for ${sha} — nothing to reset.` :
`All required e2e-test/* contexts already pass on their own for ${sha} — nothing to override.`,
);
}
if (failures.length > 0) {
throw new Error(`Failed to publish ${failures.length} E2E/Override ${state} status(es): ${failures.join('; ')}`);
}