From f416f815486b0a088c21794bbe9da2c5dde8377a Mon Sep 17 00:00:00 2001 From: Charles Bochet Date: Thu, 25 Jun 2026 11:27:51 +0200 Subject: [PATCH] fix(ci): retry Danger.js on transient GitHub API fetch errors (#22151) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem The `danger-js` job in **CI Utils** has been failing across most PRs. The failure is not a real Danger violation — it's a transient network error fetching the PR diff/commits from the GitHub API: ``` Failed to fetch GitHub pull request files: FetchError: Invalid response body while trying to fetch https://api.github.com/repos/twentyhq/twenty/pulls/XXXXX/files?page=1&per_page=100: Premature close at Gunzip. (.../node_modules/node-fetch/lib/index.js:400:12) errno: 'ERR_STREAM_PREMATURE_CLOSE', code: 'ERR_STREAM_PREMATURE_CLOSE' ``` GitHub closes the gzipped HTTP response mid-stream, and Danger's bundled `node-fetch` has **no retry** on a dropped connection — so any single blip fails the whole check. ## Why is this happening now? Nothing on our side changed at the boundary where failures started. The Node 24.16 bump landed Jun 8 (the job stayed green for 2+ weeks after), Danger has been pinned at `13.0.4` for months, and there are **zero commits** to `.nvmrc`, `twenty-utils/package.json`, or the `yarn-install` action since Jun 22. What changed is GitHub's API reset rate, and it changed abruptly: | Day | Failures | Successes | Failure rate | |-----|----------|-----------|--------------| | Jun 23 | 1 | 48 | ~2% (green) | | Jun 24 | 38 | 204 | ~16% | | Jun 25 | 23 | 25 | **~48%** | The same PR passes on one run and fails on the next (e.g. one PR shows up as both pass and fail; another failed 3 runs in a row) — a code bug can't flip outcomes on identical input, only an infrastructure flake can. When GitHub's connection-reset rate was ~0% we never noticed; now that it's in the tens of percent, roughly half of all PRs trip it. ## Fix Wrap the Danger invocation in a small retry loop (3 attempts, 5s backoff) so the check absorbs these transient fetch errors instead of red-flagging the PR. Applied to both the `danger-js` and `congratulate` jobs since they share the same failure mode. This is the correct mitigation rather than a code revert — there's no change on our side to revert. 3 attempts drop a ~48% single-shot failure rate to ~11%, and a less-degraded ~16% rate to well under 1%. If GitHub's reliability recovers, the retries simply stop firing and cost nothing. This is a CI-only change — no application code is touched. Review in cubic --- .github/workflows/ci-utils.yaml | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-utils.yaml b/.github/workflows/ci-utils.yaml index f297bb12a1..ac644fd80d 100644 --- a/.github/workflows/ci-utils.yaml +++ b/.github/workflows/ci-utils.yaml @@ -29,7 +29,22 @@ jobs: - name: Install dependencies uses: ./.github/actions/yarn-install - name: Utils / Run Danger.js - run: cd packages/twenty-utils && npx nx danger:ci + # Danger's bundled node-fetch intermittently fails to decode gzipped + # GitHub API responses with ERR_STREAM_PREMATURE_CLOSE. Retry to absorb + # those transient fetch errors instead of failing the whole check. + run: | + cd packages/twenty-utils + for attempt in 1 2 3; do + if npx nx danger:ci; then + exit 0 + fi + if [ "${attempt}" -lt 3 ]; then + echo "Danger.js attempt ${attempt} failed, retrying in 5s..." + sleep 5 + fi + done + echo "Danger.js failed after 3 attempts" + exit 1 env: DANGER_GITHUB_API_TOKEN: ${{ github.token }} @@ -42,6 +57,19 @@ jobs: - name: Install dependencies uses: ./.github/actions/yarn-install - name: Run congratulate-dangerfile.js - run: cd packages/twenty-utils && npx nx danger:congratulate + # Same transient ERR_STREAM_PREMATURE_CLOSE protection as danger-js. + run: | + cd packages/twenty-utils + for attempt in 1 2 3; do + if npx nx danger:congratulate; then + exit 0 + fi + if [ "${attempt}" -lt 3 ]; then + echo "Danger.js congratulate attempt ${attempt} failed, retrying in 5s..." + sleep 5 + fi + done + echo "Danger.js congratulate failed after 3 attempts" + exit 1 env: DANGER_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}