mirror of
https://github.com/twentyhq/twenty.git
synced 2026-08-01 10:12:06 -04:00
## 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.<anonymous> (.../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. <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22151?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-light.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
76 lines
2.6 KiB
YAML
76 lines
2.6 KiB
YAML
name: CI Utils
|
|
|
|
on:
|
|
# it's usually not recommended to use pull_request_target
|
|
# but we consider it's safe here if we keep the same steps
|
|
# see: https://securitylab.github.com/research/github-actions-preventing-pwn-requests/
|
|
# and: https://github.com/facebook/react-native/pull/34370/files
|
|
pull_request_target:
|
|
types: [opened, synchronize, reopened, closed]
|
|
|
|
permissions:
|
|
checks: write
|
|
pull-requests: write
|
|
statuses: write
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
# We don't cancel in-progress because this workflow is triggered on
|
|
# pull_request_target, which means the ref can be the same accross two PRs.
|
|
# cancel-in-progress: true
|
|
|
|
jobs:
|
|
danger-js:
|
|
timeout-minutes: 5
|
|
runs-on: ubuntu-latest
|
|
if: github.event.action != 'closed'
|
|
steps:
|
|
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
|
- name: Install dependencies
|
|
uses: ./.github/actions/yarn-install
|
|
- name: Utils / Run Danger.js
|
|
# 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 }}
|
|
|
|
congratulate:
|
|
timeout-minutes: 3
|
|
runs-on: ubuntu-latest
|
|
if: github.event.action == 'closed' && github.event.pull_request.merged == true
|
|
steps:
|
|
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
|
- name: Install dependencies
|
|
uses: ./.github/actions/yarn-install
|
|
- name: Run congratulate-dangerfile.js
|
|
# 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 }}
|