diff --git a/.github/actions/git-fetch-retry/README.md b/.github/actions/git-fetch-retry/README.md new file mode 100644 index 00000000..e948e9ff --- /dev/null +++ b/.github/actions/git-fetch-retry/README.md @@ -0,0 +1,28 @@ +# git-fetch-retry + +Composite action wrapping `git fetch` with HTTP/1.1 and bounded exponential +backoff. Durable follow-up to the runner-side global git config; use where a job +needs an `--unshallow` or extra fetch that has been observed to flake with +`curl 56` / `curl 92 HTTP/2 stream CANCEL` on the self-hosted Linux runners. + +## Use + +```yaml +- uses: actions/checkout@v4 + with: { fetch-depth: 0 } +- uses: ./.github/actions/git-fetch-retry + with: + args: "--unshallow" + retries: "5" +``` + +## Runner-side config (applied out-of-band on 198-1/-2/-3) + +``` +git config --global http.version HTTP/1.1 +git config --global http.postBuffer 524288000 +git config --global http.lowSpeedLimit 1000 +git config --global http.lowSpeedTime 300 +``` + +Revert (per host): `for k in version postBuffer lowSpeedLimit lowSpeedTime; do git config --global --unset http.$k; done` diff --git a/.github/actions/git-fetch-retry/action.yml b/.github/actions/git-fetch-retry/action.yml new file mode 100644 index 00000000..b5dad531 --- /dev/null +++ b/.github/actions/git-fetch-retry/action.yml @@ -0,0 +1,37 @@ +name: "git-fetch-retry" +description: "Resilient git fetch/unshallow: pins HTTP/1.1 and retries with bounded exponential backoff. Guards against intermittent self-hosted egress faults (curl 56 Recv failure / curl 92 HTTP/2 stream CANCEL) seen during actions/checkout --unshallow on c2pool-linux-198-*." +inputs: + args: + description: "Arguments passed verbatim to 'git fetch' (e.g. '--unshallow', 'origin master')." + required: false + default: "--unshallow" + retries: + description: "Maximum attempts before the step fails." + required: false + default: "5" + initial-delay: + description: "Seconds to wait before the first retry; doubles each attempt." + required: false + default: "5" +runs: + using: "composite" + steps: + - shell: bash + run: | + set -uo pipefail + # Belt-and-suspenders: also pin at job scope in case a runner lost the global config. + git config --global http.version HTTP/1.1 + git config --global http.postBuffer 524288000 + git config --global http.lowSpeedLimit 1000 + git config --global http.lowSpeedTime 300 + n=0; max="${{ inputs.retries }}"; delay="${{ inputs.initial-delay }}" + until git fetch ${{ inputs.args }}; do + n=$((n+1)) + if [ "$n" -ge "$max" ]; then + echo "::error::git fetch ${{ inputs.args }} failed after ${max} attempts" + exit 1 + fi + echo "::warning::git fetch attempt ${n}/${max} failed (transient egress); retrying in ${delay}s" + sleep "${delay}" + delay=$((delay*2)) + done