diff --git a/.circleci/configurations/jobs.yml b/.circleci/configurations/jobs.yml
index 1fba1d3b43d0..262278835af9 100644
--- a/.circleci/configurations/jobs.yml
+++ b/.circleci/configurations/jobs.yml
@@ -281,7 +281,9 @@ jobs:
name: Run E2E tests
command: |
cd packages/rn-tester-e2e
- yarn test-e2e ios
+ (yarn test-e2e ios 2>&1 | tee /tmp/test_log) || true
+ - store_artifacts:
+ path: /tmp/test_log
# -------------------------
# JOBS: Android E2E Tests
@@ -342,7 +344,9 @@ jobs:
name: Run E2E tests
command: |
cd packages/rn-tester-e2e
- node ../../scripts/circleci/run_with_retry.js 3 "yarn test-e2e android"
+ (yarn test-e2e android 2>&1 | tee /tmp/test_log) || true
+ - store_artifacts:
+ path: /tmp/test_log
# -------------------------
# JOBS: Build Android
diff --git a/packages/react-native-bots/dangerfile.js b/packages/react-native-bots/dangerfile.js
index 0a14b4d0fc2b..0dcb3bb24745 100644
--- a/packages/react-native-bots/dangerfile.js
+++ b/packages/react-native-bots/dangerfile.js
@@ -12,6 +12,7 @@
const {danger, fail, /*message,*/ warn} = require('danger');
const includes = require('lodash.includes');
const eslint = require('@seadub/danger-plugin-eslint');
+const fetch = require('node-fetch');
const {validate: validateChangelog} =
require('@rnx-kit/rn-changelog-generator').default;
@@ -101,3 +102,83 @@ if (isMergeRefStable) {
// Ensures that eslint is run from root folder and that it can find .eslintrc
process.chdir('../../');
eslint.default();
+
+// Wait for statuses and post a message if there are failures.
+async function handleStatuses() {
+ const regex = /Test Suites: \d+ failed/;
+ let startChecking = Date.now();
+ let done = false;
+ while (!done) {
+ let now = Date.now();
+ if (now - startChecking > 90 * 60 * 1000) {
+ warn(
+ "One hour and a half have passed and the E2E jobs haven't finished yet.",
+ );
+ done = true;
+ continue;
+ }
+
+ const githubBaseURL = `https://api.github.com/repos/${danger.github.pr.base.repo.owner.login}/${danger.github.pr.base.repo.name}`;
+ const statusesURL = `${githubBaseURL}/commits/${danger.github.pr.head.sha}/statuses?per_page=100`;
+
+ const response = await fetch(statusesURL, {
+ headers: {
+ Accept: 'application/vnd.github+json',
+ 'X-GitHub-Api-Version': '2022-11-28',
+ Authorization: `Bearer ${process.env.DANGER_GITHUB_API_TOKEN}`,
+ },
+ });
+
+ const data = await response.json();
+ const e2e_jobs = data.filter(job => {
+ return (
+ job.context === 'ci/circleci: test_e2e_ios' ||
+ job.context === 'ci/circleci: test_e2e_android'
+ );
+ });
+ if (e2e_jobs.length <= 0) {
+ console.log('No e2e jobs found yet, retrying in 5 minutes.');
+ await new Promise(resolve => setTimeout(resolve, 5 * 60 * 1000));
+ continue;
+ }
+
+ const jobFinished = e2e_jobs.every(job => job.state !== 'pending');
+ if (!jobFinished) {
+ console.log("E2E jobs haven't finished yet, retrying in 5 minutes.");
+ await new Promise(resolve => setTimeout(resolve, 5 * 60 * 1000));
+ continue;
+ }
+
+ e2e_jobs.forEach(async job => {
+ const url = job.target_url;
+ const components = url.split('/');
+ const jobId = components[components.length - 1];
+ const jobUrl = `https://circleci.com/api/v2/project/gh/facebook/react-native/${jobId}`;
+ const artifactUrl = `${jobUrl}/artifacts`;
+ const artifactResponse = await fetch(artifactUrl);
+ const artifactData = await artifactResponse.json();
+ const testLogs = artifactData.items.filter(
+ item => item.path === 'tmp/test_log',
+ );
+ if (testLogs.length !== 1) {
+ warn(
+ `Can't find the E2E test log for ${job.context}. Job link`,
+ );
+ return;
+ }
+
+ const logUrl = testLogs[0].url;
+ const logResponseText = await fetch(logUrl);
+ const logText = await logResponseText.text();
+
+ if (regex.test(logText)) {
+ warn(
+ `E2E tests for ${job.context} failed with errors. See the logs for details`,
+ );
+ }
+ });
+ done = true;
+ }
+}
+
+handleStatuses();